changes in move
This commit is contained in:
@@ -86,6 +86,7 @@ public class HierarchicalRegrouperForC {
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
List<HierarchicalActionSet> reActionSets1 = new ArrayList<>();
|
||||
for(HierarchicalActionSet a:reActionSets){
|
||||
HierarchicalActionSet hierarchicalActionSet = purifyActionSet(a);
|
||||
@@ -97,6 +98,9 @@ public class HierarchicalRegrouperForC {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return reActionSets1;
|
||||
// return reActionSets;
|
||||
}
|
||||
@@ -106,10 +110,11 @@ public class HierarchicalRegrouperForC {
|
||||
hierarchicalActionSet = removeParentForSingle(hierarchicalActionSet);
|
||||
hierarchicalActionSet = removeParentNode(hierarchicalActionSet);
|
||||
return hierarchicalActionSet;
|
||||
// return actionSet;
|
||||
}
|
||||
|
||||
private List<HierarchicalActionSet> divideBlocks(HierarchicalActionSet actionSet){
|
||||
if (actionSet.getAstNodeType().equals("block")){
|
||||
if (actionSet.getAstNodeType().equals("block") || actionSet.getAstNodeType().equals("function")){
|
||||
List<HierarchicalActionSet> subActions = actionSet.getSubActions();
|
||||
if (subActions.size() > 1) {
|
||||
boolean b = subActions.stream().allMatch(p -> NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, p.getAstNodeType()).size() == 1 && p.getAstNodeType().equals(subActions.get(0).getAstNodeType()) && p.getAction().getName().equals(subActions.get(0).getAction().getName()));
|
||||
@@ -145,6 +150,53 @@ public class HierarchicalRegrouperForC {
|
||||
// return b;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private HierarchicalActionSet removeBlocks(HierarchicalActionSet actionSet){
|
||||
List<HierarchicalActionSet> subActions = actionSet.getSubActions();
|
||||
Predicate<HierarchicalActionSet> predicate = x->NodeMap_new.getKeysByValue(NodeMap_new.StatementMap,x.getAstNodeType()).size() == 1 ;
|
||||
Predicate<HierarchicalActionSet> predicate1 = x->!x.getAstNodeType().equals("block");
|
||||
Predicate<HierarchicalActionSet> predicate2 = x->!x.getAstNodeType().equals("then");
|
||||
|
||||
|
||||
Action action = actionSet.getAction();
|
||||
if (subActions.size() == 1){
|
||||
HierarchicalActionSet subaction = subActions.get(0);
|
||||
if(!postOrder(subaction).stream().anyMatch(predicate.and(predicate1.and(predicate2)))){
|
||||
return actionSet;
|
||||
}
|
||||
Action action1 = subaction.getAction();
|
||||
//else,then,block
|
||||
if(action.getClass().equals(action1.getClass()) && action.getName().equals("UPD")) {
|
||||
|
||||
List<Integer> keysByValue = NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, subaction.getAstNodeType());
|
||||
if (keysByValue != null && keysByValue.size() == 1) {
|
||||
|
||||
subaction.setParent(null);
|
||||
return removeBlocks(subaction);
|
||||
|
||||
}
|
||||
// if(areRelatedActions(action,action1)) {
|
||||
// if (subaction.getAstNodeType().equals("block")) {//|| subaction.getAstNodeType().equals("then") || subaction.getAstNodeType().equals("else")){
|
||||
// List<HierarchicalActionSet> subSubActions = subaction.getSubActions();
|
||||
// if (subSubActions.size() == 1) {
|
||||
//
|
||||
// HierarchicalActionSet subsubsubAction = subSubActions.get(0);
|
||||
// List<Integer> keysByValue = NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, subsubsubAction.getAstNodeType());
|
||||
// if (keysByValue != null && keysByValue.size() == 1) {
|
||||
//
|
||||
// subsubsubAction.setParent(null);
|
||||
// return removeBlocks(subsubsubAction);
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
return actionSet;
|
||||
|
||||
}
|
||||
|
||||
private HierarchicalActionSet removeParentNode(HierarchicalActionSet actionSet){
|
||||
List<HierarchicalActionSet> subActions = actionSet.getSubActions();
|
||||
Action action = actionSet.getAction();
|
||||
@@ -164,42 +216,6 @@ public class HierarchicalRegrouperForC {
|
||||
return actionSet;
|
||||
}
|
||||
|
||||
private HierarchicalActionSet removeBlocks(HierarchicalActionSet actionSet){
|
||||
List<HierarchicalActionSet> subActions = actionSet.getSubActions();
|
||||
Predicate<HierarchicalActionSet> predicate = x->NodeMap_new.getKeysByValue(NodeMap_new.StatementMap,x.getAstNodeType()).size() == 1 ;
|
||||
Predicate<HierarchicalActionSet> predicate1 = x->!x.getAstNodeType().equals("block");
|
||||
|
||||
|
||||
Action action = actionSet.getAction();
|
||||
if (subActions.size() == 1){
|
||||
HierarchicalActionSet subaction = subActions.get(0);
|
||||
if(!postOrder(subaction).stream().anyMatch(predicate.and(predicate1))){
|
||||
return actionSet;
|
||||
}
|
||||
Action action1 = subaction.getAction();
|
||||
//else,then,block
|
||||
if(action.getClass().equals(action1.getClass()) && action.getName().equals("UPD")) {
|
||||
// if(areRelatedActions(action,action1)) {
|
||||
// if (subaction.getAstNodeType().equals("block")) {//|| subaction.getAstNodeType().equals("then") || subaction.getAstNodeType().equals("else")){
|
||||
List<HierarchicalActionSet> subSubActions = subaction.getSubActions();
|
||||
if (subSubActions.size() == 1) {
|
||||
|
||||
HierarchicalActionSet subsubsubAction = subSubActions.get(0);
|
||||
List<Integer> keysByValue = NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, subsubsubAction.getAstNodeType());
|
||||
if (keysByValue != null && keysByValue.size() == 1) {
|
||||
|
||||
subsubsubAction.setParent(null);
|
||||
return removeBlocks(subsubsubAction);
|
||||
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
return actionSet;
|
||||
|
||||
}
|
||||
|
||||
private HierarchicalActionSet removeParentForSingle(HierarchicalActionSet actionSet){
|
||||
List<HierarchicalActionSet> subActions = actionSet.getSubActions();
|
||||
Action action = actionSet.getAction();
|
||||
@@ -395,7 +411,12 @@ public class HierarchicalRegrouperForC {
|
||||
// }
|
||||
|
||||
for (Action act : actions) {
|
||||
if (act.getNode().equals(parent)) {
|
||||
if (act instanceof Move){
|
||||
if(act.getNode().equals(action.getNode())){
|
||||
return act;
|
||||
}
|
||||
}
|
||||
else if (act.getNode().equals(parent)) {
|
||||
if (areRelatedActions(act, action)) {
|
||||
return act;
|
||||
}
|
||||
@@ -481,9 +502,9 @@ public class HierarchicalRegrouperForC {
|
||||
}
|
||||
|
||||
private boolean areRelatedActions(Action parent, Action child) {
|
||||
if (parent instanceof Move && !(child instanceof Move)) {// If action is MOV, its children must be MOV.
|
||||
return false;
|
||||
}
|
||||
// if (parent instanceof Move && !(child instanceof Move)) {// If action is MOV, its children must be MOV.
|
||||
// return false;
|
||||
// }
|
||||
if (parent instanceof Delete && !(child instanceof Delete)) {// If action is DEL, its children must be DEL.
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -71,8 +71,9 @@ public class TestInputCases {
|
||||
// List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("wireshark_a9e926_877728_epan#dissectors#packet-ndps.c");//wrong
|
||||
// List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_041867_5d66c3_ext#pdo#pdo_dbh.c");//wrong
|
||||
// List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_d810cf_2d8fd4_Zend#zend_builtin_functions.c");//wrong
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_c56492_008fb2_sapi#phpdbg#phpdbg.c");//wrong
|
||||
|
||||
// List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_c56492_008fb2_sapi#phpdbg#phpdbg.c");//wrong
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("wireshark_96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c");//wrong
|
||||
//php-src_acd809_1d3d39_ext#standard#scanf.c
|
||||
// List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_67a100_505f61_TSRM#tsrm_virtual_cwd.c");//
|
||||
// List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("cpython_2cf5d3_9a0d7a_Objects#object.c");//
|
||||
|
||||
@@ -82,10 +83,102 @@ public class TestInputCases {
|
||||
// String s = EDiffHelper.getShapeTree(hierarchicalActionSets.get(0), false).toStaticHashString();
|
||||
// String s1 = EDiffHelper.getShapeTree(hierarchicalActionSets1.get(0), false).toStaticHashString();
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" @TO@ if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ 157870 @LENGTH@ 92\n" +
|
||||
"---UPD then@@ @TO@ item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ -1 @LENGTH@ 0\n" +
|
||||
"------MOV block@@item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @TO@ then@@ @AT@ 157973 @LENGTH@ 152\n");
|
||||
|
||||
}
|
||||
@Test
|
||||
public void test_wireshark_96ba09_755c08() throws IOException {
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("wireshark_96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c");
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" @TO@ if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ 157870 @LENGTH@ 92\n" +
|
||||
"---UPD then@@ @TO@ item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ -1 @LENGTH@ 0\n" +
|
||||
"------MOV block@@item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @TO@ then@@ @AT@ 157973 @LENGTH@ 152\n");
|
||||
|
||||
}
|
||||
@Test
|
||||
public void test_php_src_cf4a29_40becd() throws IOException {//todo upd mov with delete (get rid of delete
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_cf4a29_40becd_main#safe_mode.c");//wrong
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 0 elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename @TO@ if mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 0 elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2519 @LENGTH@ 204\n" +
|
||||
"---UPD elseif@@elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename @TO@ elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2651 @LENGTH@ 99\n" +
|
||||
"------UPD if@@if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename @TO@ if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2651 @LENGTH@ 92\n" +
|
||||
"---------UPD then@@php_error E_WARNING \"Unable to access %s\" filename @TO@ php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2696 @LENGTH@ 50\n" +
|
||||
"------------UPD block@@php_error E_WARNING \"Unable to access %s\" filename @TO@ php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2696 @LENGTH@ 50\n" +
|
||||
"---------------MOV return@@return 1 @TO@ block@@php_error E_WARNING \"Unable to access %s\" filename @AT@ 2756 @LENGTH@ 8\n");
|
||||
}
|
||||
@Test
|
||||
public void test_cpython_1a8501_c47253() throws IOException {//todo wrong
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("cpython_1a8501_c47253_Modules#signalmodule.c");//wrong
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"");
|
||||
}
|
||||
@Test
|
||||
public void test_cpython_58fb90_c9e435() throws IOException {//TODO definitely wrong
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("cpython_58fb90_c9e435_Modules#_stringio.c");//wrong
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),2);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"");
|
||||
}
|
||||
@Test
|
||||
public void test_cpython_1c76b7_d6056b() throws IOException {
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("cpython_1c76b7_d6056b_Modules#faulthandler.c");//wrong
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD function@@static void faulthandler_thread void * unused PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event PyThread_release_lock thread . cancel_event @TO@ static void faulthandler_thread void * unused PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event @AT@ 10367 @LENGTH@ 223\n" +
|
||||
"---UPD block@@PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event PyThread_release_lock thread . cancel_event @TO@ PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event @AT@ 10413 @LENGTH@ 177\n");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"MOV expr_stmt@@PyThread_release_lock thread . cancel_event @TO@ block@@break; @AT@ 11259 @LENGTH@ 43\n");
|
||||
}
|
||||
@Test
|
||||
public void test_php_src_acd809_1d3d39() throws IOException {
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_acd809_1d3d39_ext#standard#scanf.c");
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD switch@@* ch 'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @TO@ * ch 'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' 'X' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @AT@ 19173 @LENGTH@ 807\n" +
|
||||
"---UPD block@@'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @TO@ 'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' 'X' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @AT@ 19179 @LENGTH@ 802\n" +
|
||||
"------INS case@@'X' @TO@ block@@'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @AT@ 19912 @LENGTH@ 3\n" +
|
||||
"---------INS expr@@'X' @TO@ case@@'X' @AT@ 19917 @LENGTH@ 3\n" +
|
||||
"------------INS literal@@'X' @TO@ expr@@'X' @AT@ 19917 @LENGTH@ 3\n");
|
||||
}
|
||||
@Test
|
||||
public void test_lighttpd14_f27b8d_4a86d3() throws IOException {//TODO not sure
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("lighttpd1.4_f27b8d_4a86d3_src#connections.c");//wrong
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),3);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@400 505 207 break; default: con -> http_status = 501 break; @TO@ 400 414 505 207 break; default: con -> http_status = 501 break; @AT@ 8385 @LENGTH@ 59\n" +
|
||||
"---INS case@@414 @TO@ block@@400 505 207 break; default: con -> http_status = 501 break; @AT@ 8421 @LENGTH@ 3\n" +
|
||||
"------INS expr@@414 @TO@ case@@414 @AT@ 8426 @LENGTH@ 3\n" +
|
||||
"---------INS literal@@414 @TO@ expr@@414 @AT@ 8426 @LENGTH@ 3\n");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@log_error_write srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ log_error_write srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23540 @LENGTH@ 109\n" +
|
||||
"---UPD expr@@log_error_write srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ log_error_write srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23540 @LENGTH@ 109\n" +
|
||||
"------UPD call@@log_error_write srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ log_error_write srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23540 @LENGTH@ 109\n" +
|
||||
"---------UPD argument_list@@srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23555 @LENGTH@ 93\n" +
|
||||
"------------UPD argument@@\"sd\" @TO@ \"s\" @AT@ 23581 @LENGTH@ 4\n" +
|
||||
"---------------UPD expr@@\"sd\" @TO@ \"s\" @AT@ 23581 @LENGTH@ 4\n" +
|
||||
"------------------UPD literal@@\"sd\" @TO@ \"s\" @AT@ 23581 @LENGTH@ 4\n" +
|
||||
"------------UPD argument@@\"http-header larger then 64k -> disconnected\" @TO@ \"oversized request-header -> sending Status 414\" @AT@ 23587 @LENGTH@ 45\n" +
|
||||
"---------------UPD expr@@\"http-header larger then 64k -> disconnected\" @TO@ \"oversized request-header -> sending Status 414\" @AT@ 23587 @LENGTH@ 45\n" +
|
||||
"------------------UPD literal@@\"http-header larger then 64k -> disconnected\" @TO@ \"oversized request-header -> sending Status 414\" @AT@ 23587 @LENGTH@ 45\n" +
|
||||
"------------DEL argument@@chunkqueue_length cq @AT@ 23634 @LENGTH@ 20\n" +
|
||||
"---------------DEL expr@@chunkqueue_length cq @AT@ 23634 @LENGTH@ 20\n" +
|
||||
"------------------DEL call@@chunkqueue_length cq @AT@ 23634 @LENGTH@ 20\n" +
|
||||
"---------------------DEL name@@chunkqueue_length @AT@ 23634 @LENGTH@ 17\n" +
|
||||
"---------------------DEL argument_list@@cq @AT@ 23651 @LENGTH@ 2\n" +
|
||||
"------------------------DEL argument@@cq @AT@ 23652 @LENGTH@ 2\n" +
|
||||
"---------------------------DEL expr@@cq @AT@ 23652 @LENGTH@ 2\n" +
|
||||
"------------------------------DEL name@@cq @AT@ 23652 @LENGTH@ 2\n");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@connection_set_state srv con CON_STATE_REQUEST_END @TO@ connection_set_state srv con CON_STATE_HANDLE_REQUEST @AT@ 23741 @LENGTH@ 50\n" +
|
||||
"---UPD expr@@connection_set_state srv con CON_STATE_REQUEST_END @TO@ connection_set_state srv con CON_STATE_HANDLE_REQUEST @AT@ 23741 @LENGTH@ 50\n" +
|
||||
"------UPD call@@connection_set_state srv con CON_STATE_REQUEST_END @TO@ connection_set_state srv con CON_STATE_HANDLE_REQUEST @AT@ 23741 @LENGTH@ 50\n" +
|
||||
"---------UPD argument_list@@srv con CON_STATE_REQUEST_END @TO@ srv con CON_STATE_HANDLE_REQUEST @AT@ 23761 @LENGTH@ 29\n" +
|
||||
"------------UPD argument@@CON_STATE_REQUEST_END @TO@ CON_STATE_HANDLE_REQUEST @AT@ 23772 @LENGTH@ 21\n" +
|
||||
"---------------UPD expr@@CON_STATE_REQUEST_END @TO@ CON_STATE_HANDLE_REQUEST @AT@ 23772 @LENGTH@ 21\n" +
|
||||
"------------------UPD name@@CON_STATE_REQUEST_END @TO@ CON_STATE_HANDLE_REQUEST @AT@ 23772 @LENGTH@ 21\n");
|
||||
}
|
||||
@Test
|
||||
public void test_wireshark_87646b_2cc0fb() throws IOException {
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("wireshark_87646b_2cc0fb_epan#proto.c");//wrong
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
}
|
||||
@Test
|
||||
public void test_php_src_a0a56e_7afbb7() throws IOException {
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("php-src_a0a56e_7afbb7_ext#com_dotnet#com_extension.c");//ok
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
|
||||
@@ -31,12 +31,12 @@ public class TestRealCases {
|
||||
"------INS expr@@d = 0 @TO@ expr_stmt@@d = 0 @AT@ 548 @LENGTH@ 5\n" +
|
||||
"---------INS name@@d @TO@ expr@@d = 0 @AT@ 548 @LENGTH@ 1\n" +
|
||||
"---------INS operator@@= @TO@ expr@@d = 0 @AT@ 549 @LENGTH@ 1\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n" +
|
||||
"---------INS literal:number@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n" +
|
||||
"---INS expr_stmt@@h = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" return 0 @AT@ 553 @LENGTH@ 5\n" +
|
||||
"------INS expr@@h = 0 @TO@ expr_stmt@@h = 0 @AT@ 553 @LENGTH@ 5\n" +
|
||||
"---------INS name@@h @TO@ expr@@h = 0 @AT@ 553 @LENGTH@ 1\n" +
|
||||
"---------INS operator@@= @TO@ expr@@h = 0 @AT@ 554 @LENGTH@ 1\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n");
|
||||
"---------INS literal:number@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -44,17 +44,16 @@ public class TestRealCases {
|
||||
public void test_287_A_14208521_14208532() throws IOException {
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("287-A-14208521-14208532.c");
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),2);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 @TO@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 d = 0 h = 0 @AT@ 199 @LENGTH@ 347\n" +
|
||||
"---INS expr_stmt@@d = 0 @TO@ block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 @AT@ 548 @LENGTH@ 5\n" +
|
||||
"------INS expr@@d = 0 @TO@ expr_stmt@@d = 0 @AT@ 548 @LENGTH@ 5\n" +
|
||||
"---------INS name@@d @TO@ expr@@d = 0 @AT@ 548 @LENGTH@ 1\n" +
|
||||
"---------INS operator@@= @TO@ expr@@d = 0 @AT@ 549 @LENGTH@ 1\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n" +
|
||||
"---INS expr_stmt@@h = 0 @TO@ block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 @AT@ 553 @LENGTH@ 5\n" +
|
||||
"------INS expr@@h = 0 @TO@ expr_stmt@@h = 0 @AT@ 553 @LENGTH@ 5\n" +
|
||||
"---------INS name@@h @TO@ expr@@h = 0 @AT@ 553 @LENGTH@ 1\n" +
|
||||
"---------INS operator@@= @TO@ expr@@h = 0 @AT@ 554 @LENGTH@ 1\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS expr_stmt@@d = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" return 0 @AT@ 548 @LENGTH@ 5\n" +
|
||||
"---INS expr@@d = 0 @TO@ expr_stmt@@d = 0 @AT@ 548 @LENGTH@ 5\n" +
|
||||
"------INS name@@d @TO@ expr@@d = 0 @AT@ 548 @LENGTH@ 1\n" +
|
||||
"------INS operator@@= @TO@ expr@@d = 0 @AT@ 549 @LENGTH@ 1\n" +
|
||||
"------INS literal:number@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"INS expr_stmt@@h = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" return 0 @AT@ 553 @LENGTH@ 5\n" +
|
||||
"---INS expr@@h = 0 @TO@ expr_stmt@@h = 0 @AT@ 553 @LENGTH@ 5\n" +
|
||||
"------INS name@@h @TO@ expr@@h = 0 @AT@ 553 @LENGTH@ 1\n" +
|
||||
"------INS operator@@= @TO@ expr@@h = 0 @AT@ 554 @LENGTH@ 1\n" +
|
||||
"------INS literal:number@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -91,23 +90,15 @@ public class TestRealCases {
|
||||
@Test
|
||||
public void test_680_A_18343132_18343191() throws IOException {
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("680-A-18343132-18343191.c");
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ int i a ][5 b ][101 0 t 0 temp max 0 for i = 0 i < 5 i ++ scanf \"%d\" & a ][i b ][a ][i ++ t += a ][i for i = 0 i < 5 i ++ if b ][a ][i == 3 max = 3 * a ][i break; for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; printf \"%d\" t - max 0 @TO@ int i a ][5 b ][101 0 t 0 temp max 0 for i = 0 i < 5 i ++ scanf \"%d\" & a ][i b ][a ][i ++ t += a ][i for i = 0 i < 5 i ++ if b ][a ][i >= 3 max = 3 * a ][i break; for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp printf \"%d\" t - max 0 @AT@ 50 @LENGTH@ 295\n" +
|
||||
"---UPD for@@for i = 0 i < 5 i ++ if b ][a ][i == 3 max = 3 * a ][i break; @TO@ for i = 0 i < 5 i ++ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 158 @LENGTH@ 63\n" +
|
||||
"------UPD block@@ if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 173 @LENGTH@ 44\n" +
|
||||
"---------UPD if@@if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 176 @LENGTH@ 41\n" +
|
||||
"------------UPD condition@@ b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 176 @LENGTH@ 13\n" +
|
||||
"---------------UPD expr@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 177 @LENGTH@ 14\n" +
|
||||
"------------------UPD operator@@== @TO@ >= @AT@ 184 @LENGTH@ 2\n" +
|
||||
"---UPD for@@for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; @TO@ for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp @AT@ 221 @LENGTH@ 90\n" +
|
||||
"------UPD block@@ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; @TO@ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp @AT@ 236 @LENGTH@ 77\n" +
|
||||
"---------UPD if@@if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; @TO@ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp @AT@ 239 @LENGTH@ 68\n" +
|
||||
"------------UPD then@@ temp = a ][i * 2 if temp > max max = temp break; @TO@ temp = a ][i * 2 if temp > max max = temp @AT@ 253 @LENGTH@ 50\n" +
|
||||
"---------------UPD block@@ temp = a ][i * 2 if temp > max max = temp break; @TO@ temp = a ][i * 2 if temp > max max = temp @AT@ 253 @LENGTH@ 57\n" +
|
||||
"------------------UPD if@@if temp > max max = temp break; @TO@ if temp > max max = temp @AT@ 270 @LENGTH@ 32\n" +
|
||||
"---------------------UPD then@@ max = temp break; @TO@ max = temp @AT@ 282 @LENGTH@ 18\n" +
|
||||
"------------------------UPD block@@ max = temp break; @TO@ max = temp @AT@ 282 @LENGTH@ 24\n" +
|
||||
"---------------------------DEL break@@break; @AT@ 296 @LENGTH@ 6\n");
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),2);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 176 @LENGTH@ 40\n" +
|
||||
"---UPD condition@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 176 @LENGTH@ 14\n" +
|
||||
"------UPD expr@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 177 @LENGTH@ 14\n" +
|
||||
"---------UPD operator@@== @TO@ >= @AT@ 184 @LENGTH@ 2\n");
|
||||
Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"UPD if@@if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 176 @LENGTH@ 40\n" +
|
||||
"---UPD condition@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 176 @LENGTH@ 14\n" +
|
||||
"------UPD expr@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 177 @LENGTH@ 14\n" +
|
||||
"---------UPD operator@@== @TO@ >= @AT@ 184 @LENGTH@ 2\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -146,7 +137,7 @@ public class TestRealCases {
|
||||
"------------UPD index@@][j + 1 @TO@ ][j @AT@ 591 @LENGTH@ 7\n" +
|
||||
"---------------UPD expr@@[j + 1 @TO@ [j @AT@ 591 @LENGTH@ 6\n" +
|
||||
"------------------DEL operator@@+ @AT@ 592 @LENGTH@ 1\n" +
|
||||
"------------------DEL literal@@1 @AT@ 593 @LENGTH@ 1\n");
|
||||
"------------------DEL literal:number@@1 @AT@ 593 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -185,7 +176,7 @@ public class TestRealCases {
|
||||
"---------INS expr@@a == 784262 @TO@ condition@@a == 784262 @AT@ 878 @LENGTH@ 11\n" +
|
||||
"------------INS name@@a @TO@ expr@@a == 784262 @AT@ 878 @LENGTH@ 1\n" +
|
||||
"------------INS operator@@== @TO@ expr@@a == 784262 @AT@ 879 @LENGTH@ 2\n" +
|
||||
"------------INS literal@@784262 @TO@ expr@@a == 784262 @AT@ 881 @LENGTH@ 6\n" +
|
||||
"------------INS literal:number@@784262 @TO@ expr@@a == 784262 @AT@ 881 @LENGTH@ 6\n" +
|
||||
"------INS then@@printf \"4079\\n\" @TO@ if@@if a == 784262 printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" +
|
||||
"---------INS block@@printf \"4079\\n\" @TO@ then@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" +
|
||||
"------------INS expr_stmt@@printf \"4079\\n\" @TO@ block@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" +
|
||||
@@ -195,7 +186,7 @@ public class TestRealCases {
|
||||
"---------------------INS argument_list@@\"4079\\n\" @TO@ call@@printf \"4079\\n\" @AT@ 907 @LENGTH@ 8\n" +
|
||||
"------------------------INS argument@@\"4079\\n\" @TO@ argument_list@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n" +
|
||||
"---------------------------INS expr@@\"4079\\n\" @TO@ argument@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n" +
|
||||
"------------------------------INS literal@@\"4079\\n\" @TO@ expr@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n");
|
||||
"------------------------------INS literal:string@@\"4079\\n\" @TO@ expr@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n");
|
||||
|
||||
}
|
||||
@Test
|
||||
@@ -209,14 +200,14 @@ public class TestRealCases {
|
||||
"------------UPD index@@][( n + 1 ) / 2 @TO@ ][( n + 1 ) / 2 - 1 @AT@ 778 @LENGTH@ 15\n" +
|
||||
"---------------UPD expr@@[( n + 1 ) / 2 @TO@ [( n + 1 ) / 2 - 1 @AT@ 778 @LENGTH@ 14\n" +
|
||||
"------------------INS operator@@- @TO@ expr@@[( n + 1 ) / 2 @AT@ 785 @LENGTH@ 1\n" +
|
||||
"------------------INS literal@@1 @TO@ expr@@[( n + 1 ) / 2 @AT@ 786 @LENGTH@ 1\n" +
|
||||
"------------------INS literal:number@@1 @TO@ expr@@[( n + 1 ) / 2 @AT@ 786 @LENGTH@ 1\n" +
|
||||
"---INS elseif@@elseif if n == 1 printf \"0\\n\" @TO@ if@@if array ][( n + 1 ) / 2 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 @AT@ 925 @LENGTH@ 29\n" +
|
||||
"------INS if@@if n == 1 printf \"0\\n\" @TO@ elseif@@elseif if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 22\n" +
|
||||
"---------INS condition@@n == 1 @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 6\n" +
|
||||
"------------INS expr@@n == 1 @TO@ condition@@n == 1 @AT@ 926 @LENGTH@ 6\n" +
|
||||
"---------------INS name@@n @TO@ expr@@n == 1 @AT@ 926 @LENGTH@ 1\n" +
|
||||
"---------------INS operator@@== @TO@ expr@@n == 1 @AT@ 927 @LENGTH@ 2\n" +
|
||||
"---------------INS literal@@1 @TO@ expr@@n == 1 @AT@ 929 @LENGTH@ 1\n" +
|
||||
"---------------INS literal:number@@1 @TO@ expr@@n == 1 @AT@ 929 @LENGTH@ 1\n" +
|
||||
"---------INS then@@printf \"0\\n\" @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" +
|
||||
"------------INS block@@printf \"0\\n\" @TO@ then@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" +
|
||||
"---------------INS expr_stmt@@printf \"0\\n\" @TO@ block@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" +
|
||||
@@ -226,7 +217,7 @@ public class TestRealCases {
|
||||
"------------------------INS argument_list@@\"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 938 @LENGTH@ 5\n" +
|
||||
"---------------------------INS argument@@\"0\\n\" @TO@ argument_list@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n" +
|
||||
"------------------------------INS expr@@\"0\\n\" @TO@ argument@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n" +
|
||||
"---------------------------------INS literal@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n");
|
||||
"---------------------------------INS literal:string@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -243,7 +234,7 @@ public class TestRealCases {
|
||||
"------------INS expr@@[j @TO@ index@@][j @AT@ 379 @LENGTH@ 2\n" +
|
||||
"---------------INS name@@[j @TO@ expr@@[j @AT@ 379 @LENGTH@ 2\n" +
|
||||
"------INS operator@@= @TO@ expr@@a ][j = 0 @AT@ 381 @LENGTH@ 1\n" +
|
||||
"------INS literal@@0 @TO@ expr@@a ][j = 0 @AT@ 382 @LENGTH@ 1\n");
|
||||
"------INS literal:number@@0 @TO@ expr@@a ][j = 0 @AT@ 382 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -261,7 +252,7 @@ public class TestRealCases {
|
||||
"---------------INS expr@@[i @TO@ index@@][i @AT@ 243 @LENGTH@ 2\n" +
|
||||
"------------------INS name@@[i @TO@ expr@@[i @AT@ 243 @LENGTH@ 2\n" +
|
||||
"---------INS operator@@!= @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 245 @LENGTH@ 2\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 247 @LENGTH@ 1\n");
|
||||
"---------INS literal:number@@0 @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 247 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -272,12 +263,12 @@ public class TestRealCases {
|
||||
Assert.assertEquals(hierarchicalActionSets.size(),1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),
|
||||
"INS else@@else i1 = 0 @TO@ if@@if input ][0 == '.' count = 1 @AT@ 295 @LENGTH@ 11\n" +
|
||||
"---INS block@@i1 = 0 @TO@ else@@else i1 = 0 @AT@ 295 @LENGTH@ 6\n" +
|
||||
"------INS expr_stmt@@i1 = 0 @TO@ block@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" +
|
||||
"---------INS expr@@i1 = 0 @TO@ expr_stmt@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" +
|
||||
"------------INS name@@i1 @TO@ expr@@i1 = 0 @AT@ 295 @LENGTH@ 2\n" +
|
||||
"------------INS operator@@= @TO@ expr@@i1 = 0 @AT@ 297 @LENGTH@ 1\n" +
|
||||
"------------INS literal@@0 @TO@ expr@@i1 = 0 @AT@ 298 @LENGTH@ 1\n");
|
||||
"---INS block@@i1 = 0 @TO@ else@@else i1 = 0 @AT@ 295 @LENGTH@ 6\n" +
|
||||
"------INS expr_stmt@@i1 = 0 @TO@ block@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" +
|
||||
"---------INS expr@@i1 = 0 @TO@ expr_stmt@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" +
|
||||
"------------INS name@@i1 @TO@ expr@@i1 = 0 @AT@ 295 @LENGTH@ 2\n" +
|
||||
"------------INS operator@@= @TO@ expr@@i1 = 0 @AT@ 297 @LENGTH@ 1\n" +
|
||||
"------------INS literal:number@@0 @TO@ expr@@i1 = 0 @AT@ 298 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -303,7 +294,7 @@ public class TestRealCases {
|
||||
"---------UPD expr@@a < ( r1 % 10 ) @TO@ a < r1 @AT@ 193 @LENGTH@ 15\n" +
|
||||
"------------DEL operator@@( @AT@ 195 @LENGTH@ 1\n" +
|
||||
"------------DEL operator@@% @AT@ 198 @LENGTH@ 1\n" +
|
||||
"------------DEL literal@@10 @AT@ 199 @LENGTH@ 2\n" +
|
||||
"------------DEL literal:number@@10 @AT@ 199 @LENGTH@ 2\n" +
|
||||
"------------DEL operator@@) @AT@ 201 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
@@ -333,7 +324,7 @@ public class TestRealCases {
|
||||
"------UPD condition@@i < n @TO@ i < n - 1 @AT@ 163 @LENGTH@ 5\n" +
|
||||
"---------UPD expr@@i < n @TO@ i < n - 1 @AT@ 163 @LENGTH@ 5\n" +
|
||||
"------------INS operator@@- @TO@ expr@@i < n @AT@ 166 @LENGTH@ 1\n" +
|
||||
"------------INS literal@@1 @TO@ expr@@i < n @AT@ 167 @LENGTH@ 1\n" +
|
||||
"------------INS literal:number@@1 @TO@ expr@@i < n @AT@ 167 @LENGTH@ 1\n" +
|
||||
"---UPD block@@if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 173 @LENGTH@ 56\n" +
|
||||
"------UPD if@@if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 179 @LENGTH@ 56\n" +
|
||||
"---------UPD else@@else f = f + 1 i = i + 2 @TO@ else f = f + 1 @AT@ 218 @LENGTH@ 24\n" +
|
||||
@@ -344,7 +335,7 @@ public class TestRealCases {
|
||||
"---------------------DEL operator@@= @AT@ 234 @LENGTH@ 1\n" +
|
||||
"---------------------DEL name@@i @AT@ 235 @LENGTH@ 1\n" +
|
||||
"---------------------DEL operator@@+ @AT@ 236 @LENGTH@ 1\n" +
|
||||
"---------------------DEL literal@@2 @AT@ 237 @LENGTH@ 1\n");
|
||||
"---------------------DEL literal:number@@2 @AT@ 237 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
@Test
|
||||
@@ -368,10 +359,10 @@ public class TestRealCases {
|
||||
"------------------------------------UPD argument_list@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1324 @LENGTH@ 34\n" +
|
||||
"---------------------------------------UPD argument@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" +
|
||||
"------------------------------------------UPD expr@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" +
|
||||
"---------------------------------------------UPD literal@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" +
|
||||
"---------------------------------------------UPD literal:string@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" +
|
||||
"---INS return@@return 0 @TO@ block@@int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) return 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) return 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) return 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n return 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @AT@ 1385 @LENGTH@ 8\n" +
|
||||
"------INS expr@@0 @TO@ return@@return 0 @AT@ 1392 @LENGTH@ 1\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@0 @AT@ 1392 @LENGTH@ 1\n");
|
||||
"---------INS literal:number@@0 @TO@ expr@@0 @AT@ 1392 @LENGTH@ 1\n");
|
||||
|
||||
|
||||
}
|
||||
@@ -387,7 +378,7 @@ public class TestRealCases {
|
||||
"---------UPD argument_list@@ans 86401 - dp ][k @TO@ ans 86400 - dp ][k @AT@ 386 @LENGTH@ 18\n" +
|
||||
"------------UPD argument@@86401 - dp ][k @TO@ 86400 - dp ][k @AT@ 393 @LENGTH@ 14\n" +
|
||||
"---------------UPD expr@@86401 - dp ][k @TO@ 86400 - dp ][k @AT@ 393 @LENGTH@ 14\n" +
|
||||
"------------------UPD literal@@86401 @TO@ 86400 @AT@ 393 @LENGTH@ 5\n");
|
||||
"------------------UPD literal:number@@86401 @TO@ 86400 @AT@ 393 @LENGTH@ 5\n");
|
||||
|
||||
}
|
||||
@Test
|
||||
@@ -402,7 +393,7 @@ public class TestRealCases {
|
||||
"---------UPD argument_list@@negative / 2 @TO@ negative / 2.0 @AT@ 303 @LENGTH@ 12\n" +
|
||||
"------------UPD argument@@negative / 2 @TO@ negative / 2.0 @AT@ 304 @LENGTH@ 12\n" +
|
||||
"---------------UPD expr@@negative / 2 @TO@ negative / 2.0 @AT@ 304 @LENGTH@ 12\n" +
|
||||
"------------------UPD literal@@2 @TO@ 2.0 @AT@ 313 @LENGTH@ 1\n");
|
||||
"------------------UPD literal:number@@2 @TO@ 2.0 @AT@ 313 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -468,7 +459,7 @@ public class TestRealCases {
|
||||
"---------------INS expr@@[i @TO@ index@@][i @AT@ 522 @LENGTH@ 2\n" +
|
||||
"------------------INS name@@[i @TO@ expr@@[i @AT@ 522 @LENGTH@ 2\n" +
|
||||
"---------INS operator@@!= @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 524 @LENGTH@ 2\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 526 @LENGTH@ 1\n");
|
||||
"---------INS literal:number@@0 @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 526 @LENGTH@ 1\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -562,12 +553,12 @@ public class TestRealCases {
|
||||
"---------DEL expr@@i = 0 @AT@ 454 @LENGTH@ 5\n" +
|
||||
"------------DEL name@@i @AT@ 454 @LENGTH@ 1\n" +
|
||||
"------------DEL operator@@= @AT@ 455 @LENGTH@ 1\n" +
|
||||
"------------DEL literal@@0 @AT@ 456 @LENGTH@ 1\n" +
|
||||
"------------DEL literal:number@@0 @AT@ 456 @LENGTH@ 1\n" +
|
||||
"------DEL condition@@i < 10 @AT@ 458 @LENGTH@ 6\n" +
|
||||
"---------DEL expr@@i < 10 @AT@ 458 @LENGTH@ 6\n" +
|
||||
"------------DEL name@@i @AT@ 458 @LENGTH@ 1\n" +
|
||||
"------------DEL operator@@< @AT@ 459 @LENGTH@ 1\n" +
|
||||
"------------DEL literal@@10 @AT@ 460 @LENGTH@ 2\n" +
|
||||
"------------DEL literal:number@@10 @AT@ 460 @LENGTH@ 2\n" +
|
||||
"------DEL incr@@i ++ @AT@ 463 @LENGTH@ 4\n" +
|
||||
"---------DEL expr@@i ++ @AT@ 463 @LENGTH@ 4\n" +
|
||||
"------------DEL name@@i @AT@ 463 @LENGTH@ 1\n" +
|
||||
@@ -580,7 +571,7 @@ public class TestRealCases {
|
||||
"---------------DEL argument_list@@\"%d %d\\n\" num ][i tnum ][i @AT@ 483 @LENGTH@ 26\n" +
|
||||
"------------------DEL argument@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" +
|
||||
"---------------------DEL expr@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" +
|
||||
"------------------------DEL literal@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" +
|
||||
"------------------------DEL literal:string@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" +
|
||||
"------------------DEL argument@@num ][i @AT@ 494 @LENGTH@ 7\n" +
|
||||
"---------------------DEL expr@@num ][i @AT@ 494 @LENGTH@ 7\n" +
|
||||
"------------------------DEL name@@num ][i @AT@ 494 @LENGTH@ 7\n" +
|
||||
@@ -660,7 +651,7 @@ public class TestRealCases {
|
||||
"------INS expr@@max == 0 @TO@ condition@@max == 0 @AT@ 693 @LENGTH@ 8\n" +
|
||||
"---------INS name@@max @TO@ expr@@max == 0 @AT@ 693 @LENGTH@ 3\n" +
|
||||
"---------INS operator@@== @TO@ expr@@max == 0 @AT@ 696 @LENGTH@ 2\n" +
|
||||
"---------INS literal@@0 @TO@ expr@@max == 0 @AT@ 698 @LENGTH@ 1\n" +
|
||||
"---------INS literal:number@@0 @TO@ expr@@max == 0 @AT@ 698 @LENGTH@ 1\n" +
|
||||
"---INS then@@printf \"0\\n\" continue; @TO@ if@@if max == 0 printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 22\n" +
|
||||
"------INS block@@printf \"0\\n\" continue; @TO@ then@@printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 22\n" +
|
||||
"---------INS expr_stmt@@printf \"0\\n\" @TO@ block@@printf \"0\\n\" continue; @AT@ 703 @LENGTH@ 12\n" +
|
||||
@@ -670,7 +661,7 @@ public class TestRealCases {
|
||||
"------------------INS argument_list@@\"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 709 @LENGTH@ 5\n" +
|
||||
"---------------------INS argument@@\"0\\n\" @TO@ argument_list@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" +
|
||||
"------------------------INS expr@@\"0\\n\" @TO@ argument@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" +
|
||||
"---------------------------INS literal@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" +
|
||||
"---------------------------INS literal:string@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" +
|
||||
"---------INS continue@@continue; @TO@ block@@printf \"0\\n\" continue; @AT@ 717 @LENGTH@ 9\n");
|
||||
}
|
||||
|
||||
@@ -723,6 +714,46 @@ public class TestRealCases {
|
||||
"------INS operator@@= @TO@ expr@@b ][x ][y = 1 @AT@ 262 @LENGTH@ 1\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_254_B_2751488_2751528() throws IOException {
|
||||
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("254-B-2751488-2751528.c");
|
||||
Assert.assertEquals(hierarchicalActionSets.size(), 1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if d <= 0 * m = * m - 1 * d = days ][* m @TO@ if * d <= 0 * m = * m - 1 * d = days ][* m @AT@ 197 @LENGTH@ 40\n" +
|
||||
"---UPD condition@@d <= 0 @TO@ * d <= 0 @AT@ 197 @LENGTH@ 6\n" +
|
||||
"------UPD expr@@d <= 0 @TO@ * d <= 0 @AT@ 198 @LENGTH@ 6\n" +
|
||||
"---------INS operator@@* @TO@ expr@@d <= 0 @AT@ 198 @LENGTH@ 1\n");
|
||||
}
|
||||
|
||||
//codeflaws_430-C-6594918-6595299.c
|
||||
@Test
|
||||
public void test_430_C_6594918_6595299() throws IOException {
|
||||
|
||||
List<HierarchicalActionSet> hierarchicalActionSets = getHierarchicalActionSets("430-C-6594918-6595299.c");
|
||||
Assert.assertEquals(hierarchicalActionSets.size(), 1);
|
||||
Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@stk ][front ++ = nn -> v v ][nn -> v -> c = 0 if cc == 1 val ][nn -> v ++ @TO@ stk ][front ++ = nn -> v v ][nn -> v -> c = 0 val ][nn -> v += val ][stk ][rear @AT@ 1733 @LENGTH@ 73\n" +
|
||||
"---DEL if@@if cc == 1 val ][nn -> v ++ @AT@ 1832 @LENGTH@ 27\n" +
|
||||
"------DEL condition@@cc == 1 @AT@ 1832 @LENGTH@ 7\n" +
|
||||
"---------DEL expr@@cc == 1 @AT@ 1833 @LENGTH@ 7\n" +
|
||||
"------------DEL name@@cc @AT@ 1833 @LENGTH@ 2\n" +
|
||||
"------------DEL operator@@== @AT@ 1835 @LENGTH@ 2\n" +
|
||||
"------------DEL literal:number@@1 @AT@ 1837 @LENGTH@ 1\n" +
|
||||
"------DEL then@@val ][nn -> v ++ @AT@ 1864 @LENGTH@ 16\n" +
|
||||
"---------DEL block@@val ][nn -> v ++ @AT@ 1864 @LENGTH@ 16\n" +
|
||||
"---MOV expr_stmt@@val ][nn -> v ++ @TO@ block@@stk ][front ++ = nn -> v v ][nn -> v -> c = 0 if cc == 1 val ][nn -> v ++ @AT@ 1864 @LENGTH@ 16\n" +
|
||||
"------UPD expr_stmt@@val ][nn -> v ++ @TO@ val ][nn -> v += val ][stk ][rear @AT@ 1864 @LENGTH@ 16\n" +
|
||||
"---------UPD expr@@val ][nn -> v ++ @TO@ val ][nn -> v += val ][stk ][rear @AT@ 1864 @LENGTH@ 16\n" +
|
||||
"------------INS name@@val ][stk ][rear @TO@ expr@@val ][nn -> v ++ @AT@ 1842 @LENGTH@ 16\n" +
|
||||
"---------------INS name@@val @TO@ name@@val ][stk ][rear @AT@ 1842 @LENGTH@ 3\n" +
|
||||
"---------------INS index@@][stk ][rear @TO@ name@@val ][stk ][rear @AT@ 1846 @LENGTH@ 12\n" +
|
||||
"------------------INS expr@@[stk ][rear @TO@ index@@][stk ][rear @AT@ 1846 @LENGTH@ 11\n" +
|
||||
"---------------------INS name@@[stk ][rear @TO@ expr@@[stk ][rear @AT@ 1846 @LENGTH@ 11\n" +
|
||||
"------------------------INS name@@[stk @TO@ name@@[stk ][rear @AT@ 1846 @LENGTH@ 4\n" +
|
||||
"------------------------INS index@@][rear @TO@ name@@[stk ][rear @AT@ 1850 @LENGTH@ 6\n" +
|
||||
"---------------------------INS expr@@[rear @TO@ index@@][rear @AT@ 1850 @LENGTH@ 5\n" +
|
||||
"------------------------------INS name@@[rear @TO@ expr@@[rear @AT@ 1850 @LENGTH@ 5\n" +
|
||||
"------------UPD operator@@++ @TO@ += @AT@ 1874 @LENGTH@ 2\n");
|
||||
}
|
||||
public List<HierarchicalActionSet> getHierarchicalActionSets(String s) throws IOException {
|
||||
Properties appProps = new Properties();
|
||||
appProps.load(new FileInputStream("src/main/resource/app.properties"));
|
||||
|
||||
@@ -4,7 +4,8 @@ numOfWorkers = 14
|
||||
hunkLimit = 10
|
||||
patchSize = 50
|
||||
|
||||
projectList = libtiff,php-src,cpython,wireshark,gzip,gmp,lighttpd1.4,lighttpd2
|
||||
#projectList = libtiff,php-src,cpython,wireshark,gzip,gmp,lighttpd1.4,lighttpd2
|
||||
projectList = codeflaws
|
||||
|
||||
#inputPath = /Users/anilkoyuncu/projects/gumInputLinux
|
||||
#inputPath = /Users/anil.koyuncu/projects/fixminer/fixminer-data/gumInputLinux
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <stdio.h>
|
||||
int days[25]={ 0,31,29,31,30,31,30,31,31,30,31,30,31,
|
||||
31,28,31,30,31,30,31,31,30,31,30,31};
|
||||
int map[25][32]={0};
|
||||
void minus(int* m,int* d){
|
||||
*d = *d -1;
|
||||
if(d<=0){
|
||||
*m = *m - 1;
|
||||
*d = days[*m];
|
||||
}
|
||||
}
|
||||
int main(int argc,char** argv){
|
||||
int n;
|
||||
int m=0,d=0,p=0,t=0;
|
||||
int i,j,k;
|
||||
int max=-1;
|
||||
//freopen("input.txt", "r", stdin);
|
||||
//freopen("output.txt", "w", stdout);
|
||||
scanf("%d",&n);
|
||||
for(i=0;i<n;i++){
|
||||
scanf("%d %d %d %d",&m,&d,&p,&t);
|
||||
m+=12;
|
||||
minus(&m,&d);
|
||||
for(k=0;k<t;k++){
|
||||
map[m][d]+=p;
|
||||
minus(&m,&d);
|
||||
//printf("%d %d\n",m,d);
|
||||
}
|
||||
}
|
||||
/*for(i=0;i<25;i++){
|
||||
printf("%d ",i);
|
||||
for(j=0;j<32;j++){
|
||||
printf("%d ",map[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}*/
|
||||
for(i=0;i<25;i++){
|
||||
for(j=0;j<32;j++){
|
||||
if(max<map[i][j])
|
||||
max=map[i][j];
|
||||
}
|
||||
}
|
||||
printf("%d\n",max);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int cur[100010],tar[100010],val[100010],op,stk[100010],temp[100010];
|
||||
struct node{
|
||||
int v,c;
|
||||
struct node *next;
|
||||
};
|
||||
void init(int n,struct node *p[n],struct node *v[n]){
|
||||
int i=1;
|
||||
while (i<n) {
|
||||
v[i]=malloc(sizeof(struct node));
|
||||
v[i]->v=i;
|
||||
v[i]->next=NULL;
|
||||
v[i]->c=1;
|
||||
p[i]=NULL;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void create(int n,struct node *p[n],struct node *v[n],int x,int y){
|
||||
struct node *a=malloc(sizeof(struct node)),*b=malloc(sizeof(struct node));
|
||||
a->v=y;
|
||||
b->v=x;
|
||||
a->c=1;b->c=1;
|
||||
a->next=p[x];
|
||||
b->next=p[y];
|
||||
p[x]=a;
|
||||
p[y]=b;
|
||||
}
|
||||
int main(int argc, char *argv[]){
|
||||
int n,i=1;
|
||||
scanf("%d",&n);
|
||||
struct node *p[n+1],*v[n+1];
|
||||
init(n+1, p, v);
|
||||
while (i<n) {
|
||||
int a,b;
|
||||
scanf("%d %d",&a,&b);
|
||||
create(n+1, p, v, a, b);
|
||||
create(n+1, p, v, b, a);
|
||||
i++;
|
||||
}
|
||||
i=1;
|
||||
while (i<=n) {
|
||||
scanf("%d",&cur[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
i=1;
|
||||
while (i<=n) {
|
||||
scanf("%d",&tar[i]);
|
||||
i++;
|
||||
}
|
||||
//dfs(n+1, p, v, 1);
|
||||
int rear=0,front=1;
|
||||
stk[rear]=1;
|
||||
v[stk[rear]]->c=0;
|
||||
while (rear<front) {
|
||||
int cc=0;
|
||||
if(((cur[stk[rear]]+val[stk[rear]])%2)!=tar[stk[rear]]){
|
||||
val[stk[rear]]++;
|
||||
temp[op]=stk[rear];
|
||||
//printf("k %d \n",val[stk[rear]]);
|
||||
cc=1;
|
||||
op++;
|
||||
}
|
||||
struct node *n=p[stk[rear]];
|
||||
while (n!=NULL) {
|
||||
if(v[n->v]->c==1){
|
||||
stk[front++]=n->v;
|
||||
v[n->v]->c=0;
|
||||
}
|
||||
struct node *nn=p[n->v];
|
||||
while (nn!=NULL) {
|
||||
if(v[nn->v]->c==1){
|
||||
stk[front++]=nn->v;
|
||||
v[nn->v]->c=0;
|
||||
if(cc==1)
|
||||
val[nn->v]++;
|
||||
//printf("%d %d %d \n",stk[rear],nn->v,val[nn->v]);
|
||||
}
|
||||
nn=nn->next;
|
||||
}
|
||||
n=n->next;
|
||||
}
|
||||
rear++;
|
||||
}
|
||||
printf("%d\n",op);
|
||||
i=0;
|
||||
while (i<op) {
|
||||
printf("%d\n",temp[i]);
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <stdio.h>
|
||||
int days[25]={ 0,31,29,31,30,31,30,31,31,30,31,30,31,
|
||||
31,28,31,30,31,30,31,31,30,31,30,31};
|
||||
int map[25][32]={0};
|
||||
void minus(int* m,int* d){
|
||||
*d = *d -1;
|
||||
if(*d<=0){
|
||||
*m = *m - 1;
|
||||
*d = days[*m];
|
||||
}
|
||||
}
|
||||
int main(int argc,char** argv){
|
||||
int n;
|
||||
int m=0,d=0,p=0,t=0;
|
||||
int i,j,k;
|
||||
int max=-1;
|
||||
//freopen("input.txt", "r", stdin);
|
||||
//freopen("output.txt", "w", stdout);
|
||||
scanf("%d",&n);
|
||||
for(i=0;i<n;i++){
|
||||
scanf("%d %d %d %d",&m,&d,&p,&t);
|
||||
m+=12;
|
||||
minus(&m,&d);
|
||||
for(k=0;k<t;k++){
|
||||
map[m][d]+=p;
|
||||
minus(&m,&d);
|
||||
//printf("%d %d\n",m,d);
|
||||
}
|
||||
}
|
||||
/*for(i=0;i<25;i++){
|
||||
printf("%d ",i);
|
||||
for(j=0;j<32;j++){
|
||||
printf("%d ",map[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}*/
|
||||
for(i=0;i<25;i++){
|
||||
for(j=0;j<32;j++){
|
||||
if(max<map[i][j])
|
||||
max=map[i][j];
|
||||
}
|
||||
}
|
||||
printf("%d\n",max);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int cur[100010],tar[100010],val[100010],op,stk[100010],temp[100010];
|
||||
struct node{
|
||||
int v,c;
|
||||
struct node *next;
|
||||
};
|
||||
void init(int n,struct node *p[n],struct node *v[n]){
|
||||
int i=1;
|
||||
while (i<n) {
|
||||
v[i]=malloc(sizeof(struct node));
|
||||
v[i]->v=i;
|
||||
v[i]->next=NULL;
|
||||
v[i]->c=1;
|
||||
p[i]=NULL;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void create(int n,struct node *p[n],struct node *v[n],int x,int y){
|
||||
struct node *a=malloc(sizeof(struct node)),*b=malloc(sizeof(struct node));
|
||||
a->v=y;
|
||||
b->v=x;
|
||||
a->c=1;b->c=1;
|
||||
a->next=p[x];
|
||||
b->next=p[y];
|
||||
p[x]=a;
|
||||
p[y]=b;
|
||||
}
|
||||
int main(int argc, char *argv[]){
|
||||
int n,i=1;
|
||||
scanf("%d",&n);
|
||||
struct node *p[n+1],*v[n+1];
|
||||
init(n+1, p, v);
|
||||
while (i<n) {
|
||||
int a,b;
|
||||
scanf("%d %d",&a,&b);
|
||||
create(n+1, p, v, a, b);
|
||||
create(n+1, p, v, b, a);
|
||||
i++;
|
||||
}
|
||||
i=1;
|
||||
while (i<=n) {
|
||||
scanf("%d",&cur[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
i=1;
|
||||
while (i<=n) {
|
||||
scanf("%d",&tar[i]);
|
||||
i++;
|
||||
}
|
||||
//dfs(n+1, p, v, 1);
|
||||
int rear=0,front=1;
|
||||
stk[rear]=1;
|
||||
v[stk[rear]]->c=0;
|
||||
while (rear<front) {
|
||||
int cc=0;
|
||||
if(((cur[stk[rear]]+val[stk[rear]])%2)!=tar[stk[rear]]){
|
||||
val[stk[rear]]++;
|
||||
temp[op]=stk[rear];
|
||||
//printf("k %d \n",val[stk[rear]]);
|
||||
cc=1;
|
||||
op++;
|
||||
}
|
||||
struct node *n=p[stk[rear]];
|
||||
while (n!=NULL) {
|
||||
if(v[n->v]->c==1){
|
||||
stk[front++]=n->v;
|
||||
v[n->v]->c=0;
|
||||
}
|
||||
struct node *nn=p[n->v];
|
||||
while (nn!=NULL) {
|
||||
if(v[nn->v]->c==1){
|
||||
stk[front++]=nn->v;
|
||||
v[nn->v]->c=0;
|
||||
val[nn->v]+=val[stk[rear]];
|
||||
//printf("%d %d %d \n",stk[rear],nn->v,val[nn->v]);
|
||||
}
|
||||
nn=nn->next;
|
||||
}
|
||||
n=n->next;
|
||||
}
|
||||
rear++;
|
||||
}
|
||||
printf("%d\n",op);
|
||||
i=0;
|
||||
while (i<op) {
|
||||
printf("%d\n",temp[i]);
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+947
@@ -0,0 +1,947 @@
|
||||
|
||||
/* Signal module -- many thanks to Lance Ellinghaus */
|
||||
|
||||
/* XXX Signals should be recorded per thread, now we have thread state. */
|
||||
|
||||
#include "Python.h"
|
||||
#include "intrcheck.h"
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifndef SIG_ERR
|
||||
#define SIG_ERR ((PyOS_sighandler_t)(-1))
|
||||
#endif
|
||||
|
||||
#if defined(PYOS_OS2) && !defined(PYCC_GCC)
|
||||
#define NSIG 12
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#ifndef NSIG
|
||||
# if defined(_NSIG)
|
||||
# define NSIG _NSIG /* For BSD/SysV */
|
||||
# elif defined(_SIGMAX)
|
||||
# define NSIG (_SIGMAX + 1) /* For QNX */
|
||||
# elif defined(SIGMAX)
|
||||
# define NSIG (SIGMAX + 1) /* For djgpp */
|
||||
# else
|
||||
# define NSIG 64 /* Use a reasonable default value */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
NOTES ON THE INTERACTION BETWEEN SIGNALS AND THREADS
|
||||
|
||||
When threads are supported, we want the following semantics:
|
||||
|
||||
- only the main thread can set a signal handler
|
||||
- any thread can get a signal handler
|
||||
- signals are only delivered to the main thread
|
||||
|
||||
I.e. we don't support "synchronous signals" like SIGFPE (catching
|
||||
this doesn't make much sense in Python anyway) nor do we support
|
||||
signals as a means of inter-thread communication, since not all
|
||||
thread implementations support that (at least our thread library
|
||||
doesn't).
|
||||
|
||||
We still have the problem that in some implementations signals
|
||||
generated by the keyboard (e.g. SIGINT) are delivered to all
|
||||
threads (e.g. SGI), while in others (e.g. Solaris) such signals are
|
||||
delivered to one random thread (an intermediate possibility would
|
||||
be to deliver it to the main thread -- POSIX?). For now, we have
|
||||
a working implementation that works in all three cases -- the
|
||||
handler ignores signals if getpid() isn't the same as in the main
|
||||
thread. XXX This is a hack.
|
||||
|
||||
GNU pth is a user-space threading library, and as such, all threads
|
||||
run within the same process. In this case, if the currently running
|
||||
thread is not the main_thread, send the signal to the main_thread.
|
||||
*/
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
#include <sys/types.h> /* For pid_t */
|
||||
#include "pythread.h"
|
||||
static long main_thread;
|
||||
static pid_t main_pid;
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
int tripped;
|
||||
PyObject *func;
|
||||
} Handlers[NSIG];
|
||||
|
||||
static sig_atomic_t wakeup_fd = -1;
|
||||
|
||||
/* Speed up sigcheck() when none tripped */
|
||||
static volatile sig_atomic_t is_tripped = 0;
|
||||
|
||||
static PyObject *DefaultHandler;
|
||||
static PyObject *IgnoreHandler;
|
||||
static PyObject *IntHandler;
|
||||
|
||||
/* On Solaris 8, gcc will produce a warning that the function
|
||||
declaration is not a prototype. This is caused by the definition of
|
||||
SIG_DFL as (void (*)())0; the correct declaration would have been
|
||||
(void (*)(int))0. */
|
||||
|
||||
static PyOS_sighandler_t old_siginthandler = SIG_DFL;
|
||||
|
||||
#ifdef HAVE_GETITIMER
|
||||
static PyObject *ItimerError;
|
||||
|
||||
/* auxiliary functions for setitimer/getitimer */
|
||||
static void
|
||||
timeval_from_double(double d, struct timeval *tv)
|
||||
{
|
||||
tv->tv_sec = floor(d);
|
||||
tv->tv_usec = fmod(d, 1.0) * 1000000.0;
|
||||
}
|
||||
|
||||
static inline double
|
||||
double_from_timeval(struct timeval *tv)
|
||||
{
|
||||
return tv->tv_sec + (double)(tv->tv_usec / 1000000.0);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
itimer_retval(struct itimerval *iv)
|
||||
{
|
||||
PyObject *r, *v;
|
||||
|
||||
r = PyTuple_New(2);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) {
|
||||
Py_DECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(r, 0, v);
|
||||
|
||||
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) {
|
||||
Py_DECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(r, 1, v);
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
signal_default_int_handler(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyErr_SetNone(PyExc_KeyboardInterrupt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(default_int_handler_doc,
|
||||
"default_int_handler(...)\n\
|
||||
\n\
|
||||
The default handler for SIGINT installed by Python.\n\
|
||||
It raises KeyboardInterrupt.");
|
||||
|
||||
|
||||
static int
|
||||
checksignals_witharg(void * unused)
|
||||
{
|
||||
return PyErr_CheckSignals();
|
||||
}
|
||||
|
||||
static void
|
||||
signal_handler(int sig_num)
|
||||
{
|
||||
#ifdef WITH_THREAD
|
||||
#ifdef WITH_PTH
|
||||
if (PyThread_get_thread_ident() != main_thread) {
|
||||
pth_raise(*(pth_t *) main_thread, sig_num);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* See NOTES section above */
|
||||
if (getpid() == main_pid) {
|
||||
#endif
|
||||
Handlers[sig_num].tripped = 1;
|
||||
/* Set is_tripped after setting .tripped, as it gets
|
||||
cleared in PyErr_CheckSignals() before .tripped. */
|
||||
is_tripped = 1;
|
||||
Py_AddPendingCall(checksignals_witharg, NULL);
|
||||
if (wakeup_fd != -1)
|
||||
write(wakeup_fd, "\0", 1);
|
||||
#ifdef WITH_THREAD
|
||||
}
|
||||
#endif
|
||||
#ifdef SIGCHLD
|
||||
if (sig_num == SIGCHLD) {
|
||||
/* To avoid infinite recursion, this signal remains
|
||||
reset until explicit re-instated.
|
||||
Don't clear the 'func' field as it is our pointer
|
||||
to the Python handler... */
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
PyOS_setsig(sig_num, signal_handler);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_ALARM
|
||||
static PyObject *
|
||||
signal_alarm(PyObject *self, PyObject *args)
|
||||
{
|
||||
int t;
|
||||
if (!PyArg_ParseTuple(args, "i:alarm", &t))
|
||||
return NULL;
|
||||
/* alarm() returns the number of seconds remaining */
|
||||
return PyLong_FromLong((long)alarm(t));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(alarm_doc,
|
||||
"alarm(seconds)\n\
|
||||
\n\
|
||||
Arrange for SIGALRM to arrive after the given number of seconds.");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PAUSE
|
||||
static PyObject *
|
||||
signal_pause(PyObject *self)
|
||||
{
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
(void)pause();
|
||||
Py_END_ALLOW_THREADS
|
||||
/* make sure that any exceptions that got raised are propagated
|
||||
* back into Python
|
||||
*/
|
||||
if (PyErr_CheckSignals())
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
PyDoc_STRVAR(pause_doc,
|
||||
"pause()\n\
|
||||
\n\
|
||||
Wait until a signal arrives.");
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static PyObject *
|
||||
signal_signal(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *obj;
|
||||
int sig_num;
|
||||
PyObject *old_handler;
|
||||
void (*func)(int);
|
||||
if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj))
|
||||
return NULL;
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal only works in main thread");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
if (sig_num < 1 || sig_num >= NSIG) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
if (obj == IgnoreHandler)
|
||||
func = SIG_IGN;
|
||||
else if (obj == DefaultHandler)
|
||||
func = SIG_DFL;
|
||||
else if (!PyCallable_Check(obj)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
func = signal_handler;
|
||||
if (PyOS_setsig(sig_num, func) == SIG_ERR) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
return NULL;
|
||||
}
|
||||
old_handler = Handlers[sig_num].func;
|
||||
Handlers[sig_num].tripped = 0;
|
||||
Py_INCREF(obj);
|
||||
Handlers[sig_num].func = obj;
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(signal_doc,
|
||||
"signal(sig, action) -> action\n\
|
||||
\n\
|
||||
Set the action for the given signal. The action can be SIG_DFL,\n\
|
||||
SIG_IGN, or a callable Python object. The previous action is\n\
|
||||
returned. See getsignal() for possible return values.\n\
|
||||
\n\
|
||||
*** IMPORTANT NOTICE ***\n\
|
||||
A signal handler function is called with two arguments:\n\
|
||||
the first is the signal number, the second is the interrupted stack frame.");
|
||||
|
||||
|
||||
static PyObject *
|
||||
signal_getsignal(PyObject *self, PyObject *args)
|
||||
{
|
||||
int sig_num;
|
||||
PyObject *old_handler;
|
||||
if (!PyArg_ParseTuple(args, "i:getsignal", &sig_num))
|
||||
return NULL;
|
||||
if (sig_num < 1 || sig_num >= NSIG) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
old_handler = Handlers[sig_num].func;
|
||||
Py_INCREF(old_handler);
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(getsignal_doc,
|
||||
"getsignal(sig) -> action\n\
|
||||
\n\
|
||||
Return the current action for the given signal. The return value can be:\n\
|
||||
SIG_IGN -- if the signal is being ignored\n\
|
||||
SIG_DFL -- if the default action for the signal is in effect\n\
|
||||
None -- if an unknown handler is in effect\n\
|
||||
anything else -- the callable Python object used as a handler");
|
||||
|
||||
#ifdef HAVE_SIGINTERRUPT
|
||||
PyDoc_STRVAR(siginterrupt_doc,
|
||||
"siginterrupt(sig, flag) -> None\n\
|
||||
change system call restart behaviour: if flag is False, system calls\n\
|
||||
will be restarted when interrupted by signal sig, else system calls\n\
|
||||
will be interrupted.");
|
||||
|
||||
static PyObject *
|
||||
signal_siginterrupt(PyObject *self, PyObject *args)
|
||||
{
|
||||
int sig_num;
|
||||
int flag;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ii:siginterrupt", &sig_num, &flag))
|
||||
return NULL;
|
||||
if (sig_num < 1 || sig_num >= NSIG) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
if (siginterrupt(sig_num, flag)<0) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
signal_set_wakeup_fd(PyObject *self, PyObject *args)
|
||||
{
|
||||
struct stat buf;
|
||||
int fd, old_fd;
|
||||
if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd))
|
||||
return NULL;
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"set_wakeup_fd only works in main thread");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
if (fd != -1 && fstat(fd, &buf) != 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "invalid fd");
|
||||
return NULL;
|
||||
}
|
||||
old_fd = wakeup_fd;
|
||||
wakeup_fd = fd;
|
||||
return PyLong_FromLong(old_fd);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(set_wakeup_fd_doc,
|
||||
"set_wakeup_fd(fd) -> fd\n\
|
||||
\n\
|
||||
Sets the fd to be written to (with '\\0') when a signal\n\
|
||||
comes in. A library can use this to wakeup select or poll.\n\
|
||||
The previous fd is returned.\n\
|
||||
\n\
|
||||
The fd must be non-blocking.");
|
||||
|
||||
/* C API for the same, without all the error checking */
|
||||
int
|
||||
PySignal_SetWakeupFd(int fd)
|
||||
{
|
||||
int old_fd = wakeup_fd;
|
||||
if (fd < 0)
|
||||
fd = -1;
|
||||
wakeup_fd = fd;
|
||||
return old_fd;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SETITIMER
|
||||
static PyObject *
|
||||
signal_setitimer(PyObject *self, PyObject *args)
|
||||
{
|
||||
double first;
|
||||
double interval = 0;
|
||||
int which;
|
||||
struct itimerval new, old;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval))
|
||||
return NULL;
|
||||
|
||||
timeval_from_double(first, &new.it_value);
|
||||
timeval_from_double(interval, &new.it_interval);
|
||||
/* Let OS check "which" value */
|
||||
if (setitimer(which, &new, &old) != 0) {
|
||||
PyErr_SetFromErrno(ItimerError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return itimer_retval(&old);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(setitimer_doc,
|
||||
"setitimer(which, seconds[, interval])\n\
|
||||
\n\
|
||||
Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\
|
||||
or ITIMER_PROF) to fire after value seconds and after\n\
|
||||
that every interval seconds.\n\
|
||||
The itimer can be cleared by setting seconds to zero.\n\
|
||||
\n\
|
||||
Returns old values as a tuple: (delay, interval).");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_GETITIMER
|
||||
static PyObject *
|
||||
signal_getitimer(PyObject *self, PyObject *args)
|
||||
{
|
||||
int which;
|
||||
struct itimerval old;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i:getitimer", &which))
|
||||
return NULL;
|
||||
|
||||
if (getitimer(which, &old) != 0) {
|
||||
PyErr_SetFromErrno(ItimerError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return itimer_retval(&old);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(getitimer_doc,
|
||||
"getitimer(which)\n\
|
||||
\n\
|
||||
Returns current value of given itimer.");
|
||||
#endif
|
||||
|
||||
|
||||
/* List of functions defined in the module */
|
||||
static PyMethodDef signal_methods[] = {
|
||||
#ifdef HAVE_ALARM
|
||||
{"alarm", signal_alarm, METH_VARARGS, alarm_doc},
|
||||
#endif
|
||||
#ifdef HAVE_SETITIMER
|
||||
{"setitimer", signal_setitimer, METH_VARARGS, setitimer_doc},
|
||||
#endif
|
||||
#ifdef HAVE_GETITIMER
|
||||
{"getitimer", signal_getitimer, METH_VARARGS, getitimer_doc},
|
||||
#endif
|
||||
{"signal", signal_signal, METH_VARARGS, signal_doc},
|
||||
{"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc},
|
||||
{"set_wakeup_fd", signal_set_wakeup_fd, METH_VARARGS, set_wakeup_fd_doc},
|
||||
#ifdef HAVE_SIGINTERRUPT
|
||||
{"siginterrupt", signal_siginterrupt, METH_VARARGS, siginterrupt_doc},
|
||||
#endif
|
||||
#ifdef HAVE_PAUSE
|
||||
{"pause", (PyCFunction)signal_pause,
|
||||
METH_NOARGS,pause_doc},
|
||||
#endif
|
||||
{"default_int_handler", signal_default_int_handler,
|
||||
METH_VARARGS, default_int_handler_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
||||
PyDoc_STRVAR(module_doc,
|
||||
"This module provides mechanisms to use signal handlers in Python.\n\
|
||||
\n\
|
||||
Functions:\n\
|
||||
\n\
|
||||
alarm() -- cause SIGALRM after a specified time [Unix only]\n\
|
||||
setitimer() -- cause a signal (described below) after a specified\n\
|
||||
float time and the timer may restart then [Unix only]\n\
|
||||
getitimer() -- get current value of timer [Unix only]\n\
|
||||
signal() -- set the action for a given signal\n\
|
||||
getsignal() -- get the signal action for a given signal\n\
|
||||
pause() -- wait until a signal arrives [Unix only]\n\
|
||||
default_int_handler() -- default SIGINT handler\n\
|
||||
\n\
|
||||
signal constants:\n\
|
||||
SIG_DFL -- used to refer to the system default handler\n\
|
||||
SIG_IGN -- used to ignore the signal\n\
|
||||
NSIG -- number of defined signals\n\
|
||||
SIGINT, SIGTERM, etc. -- signal numbers\n\
|
||||
\n\
|
||||
itimer constants:\n\
|
||||
ITIMER_REAL -- decrements in real time, and delivers SIGALRM upon\n\
|
||||
expiration\n\
|
||||
ITIMER_VIRTUAL -- decrements only when the process is executing,\n\
|
||||
and delivers SIGVTALRM upon expiration\n\
|
||||
ITIMER_PROF -- decrements both when the process is executing and\n\
|
||||
when the system is executing on behalf of the process.\n\
|
||||
Coupled with ITIMER_VIRTUAL, this timer is usually\n\
|
||||
used to profile the time spent by the application\n\
|
||||
in user and kernel space. SIGPROF is delivered upon\n\
|
||||
expiration.\n\
|
||||
\n\n\
|
||||
*** IMPORTANT NOTICE ***\n\
|
||||
A signal handler function is called with two arguments:\n\
|
||||
the first is the signal number, the second is the interrupted stack frame.");
|
||||
|
||||
static struct PyModuleDef signalmodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"signal",
|
||||
module_doc,
|
||||
-1,
|
||||
signal_methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_signal(void)
|
||||
{
|
||||
PyObject *m, *d, *x;
|
||||
int i;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
main_thread = PyThread_get_thread_ident();
|
||||
main_pid = getpid();
|
||||
#endif
|
||||
|
||||
/* Create the module and add the functions */
|
||||
m = PyModule_Create(&signalmodule);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Add some symbolic constants to the module */
|
||||
d = PyModule_GetDict(m);
|
||||
|
||||
x = DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
|
||||
if (!x || PyDict_SetItemString(d, "SIG_DFL", x) < 0)
|
||||
goto finally;
|
||||
|
||||
x = IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN);
|
||||
if (!x || PyDict_SetItemString(d, "SIG_IGN", x) < 0)
|
||||
goto finally;
|
||||
|
||||
x = PyLong_FromLong((long)NSIG);
|
||||
if (!x || PyDict_SetItemString(d, "NSIG", x) < 0)
|
||||
goto finally;
|
||||
Py_DECREF(x);
|
||||
|
||||
x = IntHandler = PyDict_GetItemString(d, "default_int_handler");
|
||||
if (!x)
|
||||
goto finally;
|
||||
Py_INCREF(IntHandler);
|
||||
|
||||
Handlers[0].tripped = 0;
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
void (*t)(int);
|
||||
t = PyOS_getsig(i);
|
||||
Handlers[i].tripped = 0;
|
||||
if (t == SIG_DFL)
|
||||
Handlers[i].func = DefaultHandler;
|
||||
else if (t == SIG_IGN)
|
||||
Handlers[i].func = IgnoreHandler;
|
||||
else
|
||||
Handlers[i].func = Py_None; /* None of our business */
|
||||
Py_INCREF(Handlers[i].func);
|
||||
}
|
||||
if (Handlers[SIGINT].func == DefaultHandler) {
|
||||
/* Install default int handler */
|
||||
Py_INCREF(IntHandler);
|
||||
Py_DECREF(Handlers[SIGINT].func);
|
||||
Handlers[SIGINT].func = IntHandler;
|
||||
old_siginthandler = PyOS_setsig(SIGINT, signal_handler);
|
||||
}
|
||||
|
||||
#ifdef SIGHUP
|
||||
x = PyLong_FromLong(SIGHUP);
|
||||
PyDict_SetItemString(d, "SIGHUP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
x = PyLong_FromLong(SIGINT);
|
||||
PyDict_SetItemString(d, "SIGINT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGBREAK
|
||||
x = PyLong_FromLong(SIGBREAK);
|
||||
PyDict_SetItemString(d, "SIGBREAK", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGQUIT
|
||||
x = PyLong_FromLong(SIGQUIT);
|
||||
PyDict_SetItemString(d, "SIGQUIT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGILL
|
||||
x = PyLong_FromLong(SIGILL);
|
||||
PyDict_SetItemString(d, "SIGILL", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTRAP
|
||||
x = PyLong_FromLong(SIGTRAP);
|
||||
PyDict_SetItemString(d, "SIGTRAP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGIOT
|
||||
x = PyLong_FromLong(SIGIOT);
|
||||
PyDict_SetItemString(d, "SIGIOT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGABRT
|
||||
x = PyLong_FromLong(SIGABRT);
|
||||
PyDict_SetItemString(d, "SIGABRT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGEMT
|
||||
x = PyLong_FromLong(SIGEMT);
|
||||
PyDict_SetItemString(d, "SIGEMT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGFPE
|
||||
x = PyLong_FromLong(SIGFPE);
|
||||
PyDict_SetItemString(d, "SIGFPE", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGKILL
|
||||
x = PyLong_FromLong(SIGKILL);
|
||||
PyDict_SetItemString(d, "SIGKILL", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGBUS
|
||||
x = PyLong_FromLong(SIGBUS);
|
||||
PyDict_SetItemString(d, "SIGBUS", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGSEGV
|
||||
x = PyLong_FromLong(SIGSEGV);
|
||||
PyDict_SetItemString(d, "SIGSEGV", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGSYS
|
||||
x = PyLong_FromLong(SIGSYS);
|
||||
PyDict_SetItemString(d, "SIGSYS", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
x = PyLong_FromLong(SIGPIPE);
|
||||
PyDict_SetItemString(d, "SIGPIPE", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGALRM
|
||||
x = PyLong_FromLong(SIGALRM);
|
||||
PyDict_SetItemString(d, "SIGALRM", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTERM
|
||||
x = PyLong_FromLong(SIGTERM);
|
||||
PyDict_SetItemString(d, "SIGTERM", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGUSR1
|
||||
x = PyLong_FromLong(SIGUSR1);
|
||||
PyDict_SetItemString(d, "SIGUSR1", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGUSR2
|
||||
x = PyLong_FromLong(SIGUSR2);
|
||||
PyDict_SetItemString(d, "SIGUSR2", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGCLD
|
||||
x = PyLong_FromLong(SIGCLD);
|
||||
PyDict_SetItemString(d, "SIGCLD", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGCHLD
|
||||
x = PyLong_FromLong(SIGCHLD);
|
||||
PyDict_SetItemString(d, "SIGCHLD", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPWR
|
||||
x = PyLong_FromLong(SIGPWR);
|
||||
PyDict_SetItemString(d, "SIGPWR", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGIO
|
||||
x = PyLong_FromLong(SIGIO);
|
||||
PyDict_SetItemString(d, "SIGIO", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGURG
|
||||
x = PyLong_FromLong(SIGURG);
|
||||
PyDict_SetItemString(d, "SIGURG", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGWINCH
|
||||
x = PyLong_FromLong(SIGWINCH);
|
||||
PyDict_SetItemString(d, "SIGWINCH", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPOLL
|
||||
x = PyLong_FromLong(SIGPOLL);
|
||||
PyDict_SetItemString(d, "SIGPOLL", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGSTOP
|
||||
x = PyLong_FromLong(SIGSTOP);
|
||||
PyDict_SetItemString(d, "SIGSTOP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTSTP
|
||||
x = PyLong_FromLong(SIGTSTP);
|
||||
PyDict_SetItemString(d, "SIGTSTP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGCONT
|
||||
x = PyLong_FromLong(SIGCONT);
|
||||
PyDict_SetItemString(d, "SIGCONT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTTIN
|
||||
x = PyLong_FromLong(SIGTTIN);
|
||||
PyDict_SetItemString(d, "SIGTTIN", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTTOU
|
||||
x = PyLong_FromLong(SIGTTOU);
|
||||
PyDict_SetItemString(d, "SIGTTOU", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGVTALRM
|
||||
x = PyLong_FromLong(SIGVTALRM);
|
||||
PyDict_SetItemString(d, "SIGVTALRM", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPROF
|
||||
x = PyLong_FromLong(SIGPROF);
|
||||
PyDict_SetItemString(d, "SIGPROF", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGXCPU
|
||||
x = PyLong_FromLong(SIGXCPU);
|
||||
PyDict_SetItemString(d, "SIGXCPU", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGXFSZ
|
||||
x = PyLong_FromLong(SIGXFSZ);
|
||||
PyDict_SetItemString(d, "SIGXFSZ", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGRTMIN
|
||||
x = PyLong_FromLong(SIGRTMIN);
|
||||
PyDict_SetItemString(d, "SIGRTMIN", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGRTMAX
|
||||
x = PyLong_FromLong(SIGRTMAX);
|
||||
PyDict_SetItemString(d, "SIGRTMAX", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGINFO
|
||||
x = PyLong_FromLong(SIGINFO);
|
||||
PyDict_SetItemString(d, "SIGINFO", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
|
||||
#ifdef ITIMER_REAL
|
||||
x = PyLong_FromLong(ITIMER_REAL);
|
||||
PyDict_SetItemString(d, "ITIMER_REAL", x);
|
||||
Py_DECREF(x);
|
||||
#endif
|
||||
#ifdef ITIMER_VIRTUAL
|
||||
x = PyLong_FromLong(ITIMER_VIRTUAL);
|
||||
PyDict_SetItemString(d, "ITIMER_VIRTUAL", x);
|
||||
Py_DECREF(x);
|
||||
#endif
|
||||
#ifdef ITIMER_PROF
|
||||
x = PyLong_FromLong(ITIMER_PROF);
|
||||
PyDict_SetItemString(d, "ITIMER_PROF", x);
|
||||
Py_DECREF(x);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_SETITIMER) || defined (HAVE_GETITIMER)
|
||||
ItimerError = PyErr_NewException("signal.ItimerError",
|
||||
PyExc_IOError, NULL);
|
||||
if (ItimerError != NULL)
|
||||
PyDict_SetItemString(d, "ItimerError", ItimerError);
|
||||
#endif
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
Py_DECREF(m);
|
||||
m = NULL;
|
||||
}
|
||||
|
||||
finally:
|
||||
return m;
|
||||
}
|
||||
|
||||
static void
|
||||
finisignal(void)
|
||||
{
|
||||
int i;
|
||||
PyObject *func;
|
||||
|
||||
PyOS_setsig(SIGINT, old_siginthandler);
|
||||
old_siginthandler = SIG_DFL;
|
||||
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
func = Handlers[i].func;
|
||||
Handlers[i].tripped = 0;
|
||||
Handlers[i].func = NULL;
|
||||
if (i != SIGINT && func != NULL && func != Py_None &&
|
||||
func != DefaultHandler && func != IgnoreHandler)
|
||||
PyOS_setsig(i, SIG_DFL);
|
||||
Py_XDECREF(func);
|
||||
}
|
||||
|
||||
Py_XDECREF(IntHandler);
|
||||
IntHandler = NULL;
|
||||
Py_XDECREF(DefaultHandler);
|
||||
DefaultHandler = NULL;
|
||||
Py_XDECREF(IgnoreHandler);
|
||||
IgnoreHandler = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Declared in pyerrors.h */
|
||||
int
|
||||
PyErr_CheckSignals(void)
|
||||
{
|
||||
int i;
|
||||
PyObject *f;
|
||||
|
||||
if (!is_tripped)
|
||||
return 0;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The is_stripped variable is meant to speed up the calls to
|
||||
* PyErr_CheckSignals (both directly or via pending calls) when no
|
||||
* signal has arrived. This variable is set to 1 when a signal arrives
|
||||
* and it is set to 0 here, when we know some signals arrived. This way
|
||||
* we can run the registered handlers with no signals blocked.
|
||||
*
|
||||
* NOTE: with this approach we can have a situation where is_tripped is
|
||||
* 1 but we have no more signals to handle (Handlers[i].tripped
|
||||
* is 0 for every signal i). This won't do us any harm (except
|
||||
* we're gonna spent some cycles for nothing). This happens when
|
||||
* we receive a signal i after we zero is_tripped and before we
|
||||
* check Handlers[i].tripped.
|
||||
*/
|
||||
is_tripped = 0;
|
||||
|
||||
if (!(f = (PyObject *)PyEval_GetFrame()))
|
||||
f = Py_None;
|
||||
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
if (Handlers[i].tripped) {
|
||||
PyObject *result = NULL;
|
||||
PyObject *arglist = Py_BuildValue("(iO)", i, f);
|
||||
Handlers[i].tripped = 0;
|
||||
|
||||
if (arglist) {
|
||||
result = PyEval_CallObject(Handlers[i].func,
|
||||
arglist);
|
||||
Py_DECREF(arglist);
|
||||
}
|
||||
if (!result)
|
||||
return -1;
|
||||
|
||||
Py_DECREF(result);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Replacements for intrcheck.c functionality
|
||||
* Declared in pyerrors.h
|
||||
*/
|
||||
void
|
||||
PyErr_SetInterrupt(void)
|
||||
{
|
||||
is_tripped = 1;
|
||||
Handlers[SIGINT].tripped = 1;
|
||||
Py_AddPendingCall((int (*)(void *))PyErr_CheckSignals, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
PyOS_InitInterrupts(void)
|
||||
{
|
||||
PyObject *m = PyInit_signal();
|
||||
if (m) {
|
||||
_PyImport_FixupExtension(m, "signal", "signal");
|
||||
Py_DECREF(m);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PyOS_FiniInterrupts(void)
|
||||
{
|
||||
finisignal();
|
||||
}
|
||||
|
||||
int
|
||||
PyOS_InterruptOccurred(void)
|
||||
{
|
||||
if (Handlers[SIGINT].tripped) {
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread)
|
||||
return 0;
|
||||
#endif
|
||||
Handlers[SIGINT].tripped = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
PyOS_AfterFork(void)
|
||||
{
|
||||
#ifdef WITH_THREAD
|
||||
PyEval_ReInitThreads();
|
||||
main_thread = PyThread_get_thread_ident();
|
||||
main_pid = getpid();
|
||||
_PyImport_ReInitLock();
|
||||
PyThread_ReInitTLS();
|
||||
#endif
|
||||
}
|
||||
+998
@@ -0,0 +1,998 @@
|
||||
#include "Python.h"
|
||||
#include "pythread.h"
|
||||
#include <signal.h>
|
||||
#include <object.h>
|
||||
#include <frameobject.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
# define FAULTHANDLER_LATER
|
||||
#endif
|
||||
|
||||
#ifndef MS_WINDOWS
|
||||
/* register() is useless on Windows, because only SIGSEGV and SIGILL can be
|
||||
handled by the process, and these signals can only be used with enable(),
|
||||
not using register() */
|
||||
# define FAULTHANDLER_USER
|
||||
#endif
|
||||
|
||||
/* Allocate at maximum 100 MB of the stack to raise the stack overflow */
|
||||
#define STACK_OVERFLOW_MAX_SIZE (100*1024*1024)
|
||||
|
||||
#define PUTS(fd, str) write(fd, str, strlen(str))
|
||||
|
||||
#ifdef HAVE_SIGACTION
|
||||
typedef struct sigaction _Py_sighandler_t;
|
||||
#else
|
||||
typedef PyOS_sighandler_t _Py_sighandler_t;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int signum;
|
||||
int enabled;
|
||||
const char* name;
|
||||
_Py_sighandler_t previous;
|
||||
int all_threads;
|
||||
} fault_handler_t;
|
||||
|
||||
static struct {
|
||||
int enabled;
|
||||
PyObject *file;
|
||||
int fd;
|
||||
int all_threads;
|
||||
} fatal_error = {0, NULL, -1, 0};
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
static struct {
|
||||
PyObject *file;
|
||||
int fd;
|
||||
PY_TIMEOUT_T timeout_ms; /* timeout in microseconds */
|
||||
int repeat;
|
||||
volatile int running;
|
||||
PyInterpreterState *interp;
|
||||
int exit;
|
||||
/* released by parent thread when cancel request */
|
||||
PyThread_type_lock cancel_event;
|
||||
/* released by child thread when joined */
|
||||
PyThread_type_lock join_event;
|
||||
} thread;
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
typedef struct {
|
||||
int enabled;
|
||||
PyObject *file;
|
||||
int fd;
|
||||
int all_threads;
|
||||
_Py_sighandler_t previous;
|
||||
} user_signal_t;
|
||||
|
||||
static user_signal_t *user_signals;
|
||||
|
||||
/* the following macros come from Python: Modules/signalmodule.c */
|
||||
#if defined(PYOS_OS2) && !defined(PYCC_GCC)
|
||||
#define NSIG 12
|
||||
#endif
|
||||
#ifndef NSIG
|
||||
# if defined(_NSIG)
|
||||
# define NSIG _NSIG /* For BSD/SysV */
|
||||
# elif defined(_SIGMAX)
|
||||
# define NSIG (_SIGMAX + 1) /* For QNX */
|
||||
# elif defined(SIGMAX)
|
||||
# define NSIG (SIGMAX + 1) /* For djgpp */
|
||||
# else
|
||||
# define NSIG 64 /* Use a reasonable default value */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* FAULTHANDLER_USER */
|
||||
|
||||
|
||||
static fault_handler_t faulthandler_handlers[] = {
|
||||
#ifdef SIGBUS
|
||||
{SIGBUS, 0, "Bus error", },
|
||||
#endif
|
||||
#ifdef SIGILL
|
||||
{SIGILL, 0, "Illegal instruction", },
|
||||
#endif
|
||||
{SIGFPE, 0, "Floating point exception", },
|
||||
/* define SIGSEGV at the end to make it the default choice if searching the
|
||||
handler fails in faulthandler_fatal_error() */
|
||||
{SIGSEGV, 0, "Segmentation fault", }
|
||||
};
|
||||
static const unsigned char faulthandler_nsignals = \
|
||||
sizeof(faulthandler_handlers) / sizeof(faulthandler_handlers[0]);
|
||||
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
static stack_t stack;
|
||||
#endif
|
||||
|
||||
|
||||
/* Get the file descriptor of a file by calling its fileno() method and then
|
||||
call its flush() method.
|
||||
|
||||
If file is NULL or Py_None, use sys.stderr as the new file.
|
||||
|
||||
On success, return the new file and write the file descriptor into *p_fd.
|
||||
On error, return NULL. */
|
||||
|
||||
static PyObject*
|
||||
faulthandler_get_fileno(PyObject *file, int *p_fd)
|
||||
{
|
||||
PyObject *result;
|
||||
long fd_long;
|
||||
int fd;
|
||||
|
||||
if (file == NULL || file == Py_None) {
|
||||
file = PySys_GetObject("stderr");
|
||||
if (file == NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.stderr");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
result = PyObject_CallMethod(file, "fileno", "");
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
fd = -1;
|
||||
if (PyLong_Check(result)) {
|
||||
fd_long = PyLong_AsLong(result);
|
||||
if (0 <= fd_long && fd_long < INT_MAX)
|
||||
fd = (int)fd_long;
|
||||
}
|
||||
Py_DECREF(result);
|
||||
|
||||
if (fd == -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"file.fileno() is not a valid file descriptor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PyObject_CallMethod(file, "flush", "");
|
||||
if (result != NULL)
|
||||
Py_DECREF(result);
|
||||
else {
|
||||
/* ignore flush() error */
|
||||
PyErr_Clear();
|
||||
}
|
||||
*p_fd = fd;
|
||||
return file;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_dump_traceback_py(PyObject *self,
|
||||
PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"file", "all_threads", NULL};
|
||||
PyObject *file = NULL;
|
||||
int all_threads = 0;
|
||||
PyThreadState *tstate;
|
||||
const char *errmsg;
|
||||
int fd;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"|Oi:dump_traceback", kwlist,
|
||||
&file, &all_threads))
|
||||
return NULL;
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
/* The caller holds the GIL and so PyThreadState_Get() can be used */
|
||||
tstate = PyThreadState_Get();
|
||||
if (tstate == NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"unable to get the current thread state");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (all_threads) {
|
||||
errmsg = _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
|
||||
if (errmsg != NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError, errmsg);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_Py_DumpTraceback(fd, tstate);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
/* Handler of SIGSEGV, SIGFPE, SIGBUS and SIGILL signals.
|
||||
|
||||
Display the current Python traceback, restore the previous handler and call
|
||||
the previous handler.
|
||||
|
||||
On Windows, don't call explictly the previous handler, because Windows
|
||||
signal handler would not be called (for an unknown reason). The execution of
|
||||
the program continues at faulthandler_fatal_error() exit, but the same
|
||||
instruction will raise the same fault (signal), and so the previous handler
|
||||
will be called.
|
||||
|
||||
This function is signal safe and should only call signal safe functions. */
|
||||
|
||||
static void
|
||||
faulthandler_fatal_error(
|
||||
int signum
|
||||
#ifdef HAVE_SIGACTION
|
||||
, siginfo_t *siginfo, void *ucontext
|
||||
#endif
|
||||
)
|
||||
{
|
||||
const int fd = fatal_error.fd;
|
||||
unsigned int i;
|
||||
fault_handler_t *handler = NULL;
|
||||
PyThreadState *tstate;
|
||||
|
||||
if (!fatal_error.enabled)
|
||||
return;
|
||||
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
handler = &faulthandler_handlers[i];
|
||||
if (handler->signum == signum)
|
||||
break;
|
||||
}
|
||||
if (handler == NULL) {
|
||||
/* faulthandler_nsignals == 0 (unlikely) */
|
||||
return;
|
||||
}
|
||||
|
||||
/* restore the previous handler */
|
||||
#ifdef HAVE_SIGACTION
|
||||
(void)sigaction(handler->signum, &handler->previous, NULL);
|
||||
#else
|
||||
(void)signal(handler->signum, handler->previous);
|
||||
#endif
|
||||
handler->enabled = 0;
|
||||
|
||||
PUTS(fd, "Fatal Python error: ");
|
||||
PUTS(fd, handler->name);
|
||||
PUTS(fd, "\n\n");
|
||||
|
||||
/* SIGSEGV, SIGFPE, SIGBUS and SIGILL are synchronous signals and so are
|
||||
delivered to the thread that caused the fault. Get the Python thread
|
||||
state of the current thread.
|
||||
|
||||
PyThreadState_Get() doesn't give the state of the thread that caused the
|
||||
fault if the thread released the GIL, and so this function cannot be
|
||||
used. Read the thread local storage (TLS) instead: call
|
||||
PyGILState_GetThisThreadState(). */
|
||||
tstate = PyGILState_GetThisThreadState();
|
||||
if (tstate == NULL)
|
||||
return;
|
||||
|
||||
if (fatal_error.all_threads)
|
||||
_Py_DumpTracebackThreads(fd, tstate->interp, tstate);
|
||||
else
|
||||
_Py_DumpTraceback(fd, tstate);
|
||||
|
||||
#ifndef MS_WINDOWS
|
||||
/* call the previous signal handler: it is called if we use sigaction()
|
||||
thanks to SA_NODEFER flag, otherwise it is deferred */
|
||||
raise(signum);
|
||||
#else
|
||||
/* on Windows, don't call explictly the previous handler, because Windows
|
||||
signal handler would not be called */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Install handler for fatal signals (SIGSEGV, SIGFPE, ...). */
|
||||
|
||||
static PyObject*
|
||||
faulthandler_enable(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"file", "all_threads", NULL};
|
||||
PyObject *file = NULL;
|
||||
int all_threads = 0;
|
||||
unsigned int i;
|
||||
fault_handler_t *handler;
|
||||
#ifdef HAVE_SIGACTION
|
||||
struct sigaction action;
|
||||
#endif
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"|Oi:enable", kwlist, &file, &all_threads))
|
||||
return NULL;
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
Py_XDECREF(fatal_error.file);
|
||||
Py_INCREF(file);
|
||||
fatal_error.file = file;
|
||||
fatal_error.fd = fd;
|
||||
fatal_error.all_threads = all_threads;
|
||||
|
||||
if (!fatal_error.enabled) {
|
||||
fatal_error.enabled = 1;
|
||||
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
handler = &faulthandler_handlers[i];
|
||||
#ifdef HAVE_SIGACTION
|
||||
action.sa_sigaction = faulthandler_fatal_error;
|
||||
sigemptyset(&action.sa_mask);
|
||||
/* Do not prevent the signal from being received from within
|
||||
its own signal handler */
|
||||
action.sa_flags = SA_NODEFER;
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
if (stack.ss_sp != NULL) {
|
||||
/* Call the signal handler on an alternate signal stack
|
||||
provided by sigaltstack() */
|
||||
action.sa_flags |= SA_ONSTACK;
|
||||
}
|
||||
#endif
|
||||
err = sigaction(handler->signum, &action, &handler->previous);
|
||||
#else
|
||||
handler->previous = signal(handler->signum,
|
||||
faulthandler_fatal_error);
|
||||
err = (handler->previous == SIG_ERR);
|
||||
#endif
|
||||
if (err) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
return NULL;
|
||||
}
|
||||
handler->enabled = 1;
|
||||
}
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
faulthandler_disable(void)
|
||||
{
|
||||
unsigned int i;
|
||||
fault_handler_t *handler;
|
||||
|
||||
if (fatal_error.enabled) {
|
||||
fatal_error.enabled = 0;
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
handler = &faulthandler_handlers[i];
|
||||
if (!handler->enabled)
|
||||
continue;
|
||||
#ifdef HAVE_SIGACTION
|
||||
(void)sigaction(handler->signum, &handler->previous, NULL);
|
||||
#else
|
||||
(void)signal(handler->signum, handler->previous);
|
||||
#endif
|
||||
handler->enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Py_CLEAR(fatal_error.file);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_disable_py(PyObject *self)
|
||||
{
|
||||
if (!fatal_error.enabled) {
|
||||
Py_INCREF(Py_False);
|
||||
return Py_False;
|
||||
}
|
||||
faulthandler_disable();
|
||||
Py_INCREF(Py_True);
|
||||
return Py_True;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_is_enabled(PyObject *self)
|
||||
{
|
||||
return PyBool_FromLong(fatal_error.enabled);
|
||||
}
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
|
||||
static void
|
||||
faulthandler_thread(void *unused)
|
||||
{
|
||||
PyLockStatus st;
|
||||
const char* errmsg;
|
||||
PyThreadState *current;
|
||||
int ok;
|
||||
|
||||
do {
|
||||
st = PyThread_acquire_lock_timed(thread.cancel_event,
|
||||
thread.timeout_ms, 0);
|
||||
if (st == PY_LOCK_ACQUIRED) {
|
||||
/* Cancelled by user */
|
||||
break;
|
||||
}
|
||||
/* Timeout => dump traceback */
|
||||
assert(st == PY_LOCK_FAILURE);
|
||||
|
||||
/* get the thread holding the GIL, NULL if no thread hold the GIL */
|
||||
current = _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
|
||||
errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current);
|
||||
ok = (errmsg == NULL);
|
||||
|
||||
if (thread.exit)
|
||||
_exit(1);
|
||||
} while (ok && thread.repeat);
|
||||
|
||||
/* The only way out */
|
||||
thread.running = 0;
|
||||
PyThread_release_lock(thread.join_event);
|
||||
PyThread_release_lock(thread.cancel_event);
|
||||
}
|
||||
|
||||
static void
|
||||
faulthandler_cancel_dump_tracebacks_later(void)
|
||||
{
|
||||
if (thread.running) {
|
||||
/* Notify cancellation */
|
||||
PyThread_release_lock(thread.cancel_event);
|
||||
/* Wait for thread to join */
|
||||
PyThread_acquire_lock(thread.join_event, 1);
|
||||
assert(thread.running == 0);
|
||||
PyThread_release_lock(thread.join_event);
|
||||
}
|
||||
Py_CLEAR(thread.file);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_dump_traceback_later(PyObject *self,
|
||||
PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL};
|
||||
double timeout;
|
||||
PY_TIMEOUT_T timeout_ms;
|
||||
int repeat = 0;
|
||||
PyObject *file = NULL;
|
||||
int fd;
|
||||
int exit = 0;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"d|iOi:dump_tracebacks_later", kwlist,
|
||||
&timeout, &repeat, &file, &exit))
|
||||
return NULL;
|
||||
timeout *= 1e6;
|
||||
if (timeout >= (double) PY_TIMEOUT_MAX) {
|
||||
PyErr_SetString(PyExc_OverflowError, "timeout value is too large");
|
||||
return NULL;
|
||||
}
|
||||
timeout_ms = (PY_TIMEOUT_T)timeout;
|
||||
if (timeout_ms <= 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Cancel previous thread, if running */
|
||||
faulthandler_cancel_dump_tracebacks_later();
|
||||
|
||||
Py_XDECREF(thread.file);
|
||||
Py_INCREF(file);
|
||||
thread.file = file;
|
||||
thread.fd = fd;
|
||||
thread.timeout_ms = timeout_ms;
|
||||
thread.repeat = repeat;
|
||||
thread.interp = PyThreadState_Get()->interp;
|
||||
thread.exit = exit;
|
||||
|
||||
/* Arm these locks to serve as events when released */
|
||||
PyThread_acquire_lock(thread.join_event, 1);
|
||||
PyThread_acquire_lock(thread.cancel_event, 1);
|
||||
|
||||
thread.running = 1;
|
||||
if (PyThread_start_new_thread(faulthandler_thread, NULL) == -1) {
|
||||
thread.running = 0;
|
||||
Py_CLEAR(thread.file);
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"unable to start watchdog thread");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_cancel_dump_tracebacks_later_py(PyObject *self)
|
||||
{
|
||||
faulthandler_cancel_dump_tracebacks_later();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif /* FAULTHANDLER_LATER */
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
/* Handler of user signals (e.g. SIGUSR1).
|
||||
|
||||
Dump the traceback of the current thread, or of all threads if
|
||||
thread.all_threads is true.
|
||||
|
||||
This function is signal safe and should only call signal safe functions. */
|
||||
|
||||
static void
|
||||
faulthandler_user(int signum)
|
||||
{
|
||||
user_signal_t *user;
|
||||
PyThreadState *tstate;
|
||||
|
||||
user = &user_signals[signum];
|
||||
if (!user->enabled)
|
||||
return;
|
||||
|
||||
/* PyThreadState_Get() doesn't give the state of the current thread if
|
||||
the thread doesn't hold the GIL. Read the thread local storage (TLS)
|
||||
instead: call PyGILState_GetThisThreadState(). */
|
||||
tstate = PyGILState_GetThisThreadState();
|
||||
if (tstate == NULL) {
|
||||
/* unable to get the current thread, do nothing */
|
||||
return;
|
||||
}
|
||||
|
||||
if (user->all_threads)
|
||||
_Py_DumpTracebackThreads(user->fd, tstate->interp, tstate);
|
||||
else
|
||||
_Py_DumpTraceback(user->fd, tstate);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_register(PyObject *self,
|
||||
PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"signum", "file", "all_threads", NULL};
|
||||
int signum;
|
||||
PyObject *file = NULL;
|
||||
int all_threads = 0;
|
||||
int fd;
|
||||
unsigned int i;
|
||||
user_signal_t *user;
|
||||
_Py_sighandler_t previous;
|
||||
#ifdef HAVE_SIGACTION
|
||||
struct sigaction action;
|
||||
#endif
|
||||
int err;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"i|Oi:register", kwlist,
|
||||
&signum, &file, &all_threads))
|
||||
return NULL;
|
||||
|
||||
if (signum < 1 || NSIG <= signum) {
|
||||
PyErr_SetString(PyExc_ValueError, "signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
if (faulthandler_handlers[i].signum == signum) {
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"signal %i cannot be registered by register(), "
|
||||
"use enable() instead",
|
||||
signum);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
if (user_signals == NULL) {
|
||||
user_signals = calloc(NSIG, sizeof(user_signal_t));
|
||||
if (user_signals == NULL)
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
user = &user_signals[signum];
|
||||
|
||||
if (!user->enabled) {
|
||||
#ifdef HAVE_SIGACTION
|
||||
action.sa_handler = faulthandler_user;
|
||||
sigemptyset(&action.sa_mask);
|
||||
/* if the signal is received while the kernel is executing a system
|
||||
call, try to restart the system call instead of interrupting it and
|
||||
return EINTR */
|
||||
action.sa_flags = SA_RESTART;
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
if (stack.ss_sp != NULL) {
|
||||
/* Call the signal handler on an alternate signal stack
|
||||
provided by sigaltstack() */
|
||||
action.sa_flags |= SA_ONSTACK;
|
||||
}
|
||||
#endif
|
||||
err = sigaction(signum, &action, &previous);
|
||||
#else
|
||||
previous = signal(signum, faulthandler_user);
|
||||
err = (previous == SIG_ERR);
|
||||
#endif
|
||||
if (err) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Py_XDECREF(user->file);
|
||||
Py_INCREF(file);
|
||||
user->file = file;
|
||||
user->fd = fd;
|
||||
user->all_threads = all_threads;
|
||||
user->previous = previous;
|
||||
user->enabled = 1;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
faulthandler_unregister(user_signal_t *user, int signum)
|
||||
{
|
||||
if (user->enabled)
|
||||
return 0;
|
||||
user->enabled = 0;
|
||||
#ifdef HAVE_SIGACTION
|
||||
(void)sigaction(signum, &user->previous, NULL);
|
||||
#else
|
||||
(void)signal(signum, user->previous);
|
||||
#endif
|
||||
Py_CLEAR(user->file);
|
||||
user->fd = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_unregister_py(PyObject *self, PyObject *args)
|
||||
{
|
||||
int signum;
|
||||
user_signal_t *user;
|
||||
int change;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i:unregister", &signum))
|
||||
return NULL;
|
||||
|
||||
if (signum < 1 || NSIG <= signum) {
|
||||
PyErr_SetString(PyExc_ValueError, "signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user = &user_signals[signum];
|
||||
change = faulthandler_unregister(user, signum);
|
||||
return PyBool_FromLong(change);
|
||||
}
|
||||
#endif /* FAULTHANDLER_USER */
|
||||
|
||||
|
||||
static PyObject *
|
||||
faulthandler_read_null(PyObject *self, PyObject *args)
|
||||
{
|
||||
int *x = NULL, y;
|
||||
int release_gil = 0;
|
||||
if (!PyArg_ParseTuple(args, "|i:_read_null", &release_gil))
|
||||
return NULL;
|
||||
if (release_gil) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
y = *x;
|
||||
Py_END_ALLOW_THREADS
|
||||
} else
|
||||
y = *x;
|
||||
return PyLong_FromLong(y);
|
||||
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
faulthandler_sigsegv(PyObject *self, PyObject *args)
|
||||
{
|
||||
#if defined(MS_WINDOWS)
|
||||
/* faulthandler_fatal_error() restores the previous signal handler and then
|
||||
gives back the execution flow to the program. In a normal case, the
|
||||
SIGSEGV was raised by the kernel because of a fault, and so if the
|
||||
program retries to execute the same instruction, the fault will be
|
||||
raised again.
|
||||
|
||||
Here the fault is simulated by a fake SIGSEGV signal raised by the
|
||||
application. We have to raise SIGSEGV at lease twice: once for
|
||||
faulthandler_fatal_error(), and one more time for the previous signal
|
||||
handler. */
|
||||
while(1)
|
||||
raise(SIGSEGV);
|
||||
#else
|
||||
raise(SIGSEGV);
|
||||
#endif
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
faulthandler_sigfpe(PyObject *self, PyObject *args)
|
||||
{
|
||||
/* Do an integer division by zero: raise a SIGFPE on Intel CPU, but not on
|
||||
PowerPC. Use volatile to disable compile-time optimizations. */
|
||||
volatile int x = 1, y = 0, z;
|
||||
z = x / y;
|
||||
/* if the division by zero didn't raise a SIGFPE, raise it manually */
|
||||
raise(SIGFPE);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef SIGBUS
|
||||
static PyObject *
|
||||
faulthandler_sigbus(PyObject *self, PyObject *args)
|
||||
{
|
||||
raise(SIGBUS);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SIGILL
|
||||
static PyObject *
|
||||
faulthandler_sigill(PyObject *self, PyObject *args)
|
||||
{
|
||||
#if defined(MS_WINDOWS)
|
||||
/* see faulthandler_sigsegv() for the explanation about while(1) */
|
||||
while(1)
|
||||
raise(SIGILL);
|
||||
#else
|
||||
raise(SIGILL);
|
||||
#endif
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
faulthandler_fatal_error_py(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *message;
|
||||
if (!PyArg_ParseTuple(args, "y:fatal_error", &message))
|
||||
return NULL;
|
||||
Py_FatalError(message);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
|
||||
void*
|
||||
stack_overflow(void *min_sp, void *max_sp, size_t *depth)
|
||||
{
|
||||
/* allocate 4096 bytes on the stack at each call */
|
||||
unsigned char buffer[4096];
|
||||
void *sp = &buffer;
|
||||
*depth += 1;
|
||||
if (sp < min_sp || max_sp < sp)
|
||||
return sp;
|
||||
buffer[0] = 1;
|
||||
buffer[4095] = 0;
|
||||
return stack_overflow(min_sp, max_sp, depth);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
faulthandler_stack_overflow(PyObject *self)
|
||||
{
|
||||
size_t depth, size;
|
||||
void *sp = &depth, *stop;
|
||||
|
||||
depth = 0;
|
||||
stop = stack_overflow(sp - STACK_OVERFLOW_MAX_SIZE,
|
||||
sp + STACK_OVERFLOW_MAX_SIZE,
|
||||
&depth);
|
||||
if (sp < stop)
|
||||
size = stop - sp;
|
||||
else
|
||||
size = sp - stop;
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"unable to raise a stack overflow (allocated %zu bytes "
|
||||
"on the stack, %zu recursive calls)",
|
||||
size, depth);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
faulthandler_traverse(PyObject *module, visitproc visit, void *arg)
|
||||
{
|
||||
#ifdef FAULTHANDLER_USER
|
||||
unsigned int index;
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
Py_VISIT(thread.file);
|
||||
#endif
|
||||
#ifdef FAULTHANDLER_USER
|
||||
if (user_signals != NULL) {
|
||||
for (index=0; index < NSIG; index++)
|
||||
Py_VISIT(user_signals[index].file);
|
||||
}
|
||||
#endif
|
||||
Py_VISIT(fatal_error.file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(module_doc,
|
||||
"faulthandler module.");
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"enable",
|
||||
(PyCFunction)faulthandler_enable, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("enable(file=sys.stderr, all_threads=False): "
|
||||
"enable the fault handler")},
|
||||
{"disable", (PyCFunction)faulthandler_disable_py, METH_NOARGS,
|
||||
PyDoc_STR("disable(): disable the fault handler")},
|
||||
{"is_enabled", (PyCFunction)faulthandler_is_enabled, METH_NOARGS,
|
||||
PyDoc_STR("is_enabled()->bool: check if the handler is enabled")},
|
||||
{"dump_traceback",
|
||||
(PyCFunction)faulthandler_dump_traceback_py, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=False): "
|
||||
"dump the traceback of the current thread, or of all threads "
|
||||
"if all_threads is True, into file")},
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
{"dump_tracebacks_later",
|
||||
(PyCFunction)faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("dump_tracebacks_later(timeout, repeat=False, file=sys.stderr):\n"
|
||||
"dump the traceback of all threads in timeout seconds,\n"
|
||||
"or each timeout seconds if repeat is True.")},
|
||||
{"cancel_dump_tracebacks_later",
|
||||
(PyCFunction)faulthandler_cancel_dump_tracebacks_later_py, METH_NOARGS,
|
||||
PyDoc_STR("cancel_dump_tracebacks_later():\ncancel the previous call "
|
||||
"to dump_tracebacks_later().")},
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
{"register",
|
||||
(PyCFunction)faulthandler_register, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("register(signum, file=sys.stderr, all_threads=False): "
|
||||
"register an handler for the signal 'signum': dump the "
|
||||
"traceback of the current thread, or of all threads if "
|
||||
"all_threads is True, into file")},
|
||||
{"unregister",
|
||||
faulthandler_unregister_py, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("unregister(signum): unregister the handler of the signal "
|
||||
"'signum' registered by register()")},
|
||||
#endif
|
||||
|
||||
{"_read_null", faulthandler_read_null, METH_VARARGS,
|
||||
PyDoc_STR("_read_null(release_gil=False): read from NULL, raise "
|
||||
"a SIGSEGV or SIGBUS signal depending on the platform")},
|
||||
{"_sigsegv", faulthandler_sigsegv, METH_VARARGS,
|
||||
PyDoc_STR("_sigsegv(): raise a SIGSEGV signal")},
|
||||
{"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS,
|
||||
PyDoc_STR("_sigfpe(): raise a SIGFPE signal")},
|
||||
#ifdef SIGBUS
|
||||
{"_sigbus", (PyCFunction)faulthandler_sigbus, METH_NOARGS,
|
||||
PyDoc_STR("_sigbus(): raise a SIGBUS signal")},
|
||||
#endif
|
||||
#ifdef SIGILL
|
||||
{"_sigill", (PyCFunction)faulthandler_sigill, METH_NOARGS,
|
||||
PyDoc_STR("_sigill(): raise a SIGILL signal")},
|
||||
#endif
|
||||
{"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS,
|
||||
PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")},
|
||||
#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
|
||||
{"_stack_overflow", (PyCFunction)faulthandler_stack_overflow, METH_NOARGS,
|
||||
PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")},
|
||||
#endif
|
||||
{NULL, NULL} /* terminator */
|
||||
};
|
||||
|
||||
static struct PyModuleDef module_def = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"faulthandler",
|
||||
module_doc,
|
||||
0, /* non negative size to be able to unload the module */
|
||||
module_methods,
|
||||
NULL,
|
||||
faulthandler_traverse,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_faulthandler(void)
|
||||
{
|
||||
return PyModule_Create(&module_def);
|
||||
}
|
||||
|
||||
/* Call faulthandler.enable() if PYTHONFAULTHANDLER environment variable is
|
||||
defined, or if sys._xoptions has a 'faulthandler' key. */
|
||||
|
||||
static int
|
||||
faulthandler_env_options(void)
|
||||
{
|
||||
PyObject *xoptions, *key, *module, *res;
|
||||
int enable;
|
||||
|
||||
if (!Py_GETENV("PYTHONFAULTHANDLER")) {
|
||||
xoptions = PySys_GetXOptions();
|
||||
if (xoptions == NULL)
|
||||
return -1;
|
||||
|
||||
key = PyUnicode_FromString("faulthandler");
|
||||
if (key == NULL)
|
||||
return -1;
|
||||
|
||||
enable = PyDict_Contains(xoptions, key);
|
||||
Py_DECREF(key);
|
||||
if (!enable)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
enable = 1;
|
||||
|
||||
module = PyImport_ImportModule("faulthandler");
|
||||
if (module == NULL) {
|
||||
return -1;
|
||||
}
|
||||
res = PyObject_CallMethod(module, "enable", "");
|
||||
Py_DECREF(module);
|
||||
if (res == NULL)
|
||||
return -1;
|
||||
Py_DECREF(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _PyFaulthandler_Init(void)
|
||||
{
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
int err;
|
||||
|
||||
/* Try to allocate an alternate stack for faulthandler() signal handler to
|
||||
* be able to allocate memory on the stack, even on a stack overflow. If it
|
||||
* fails, ignore the error. */
|
||||
stack.ss_flags = 0;
|
||||
stack.ss_size = SIGSTKSZ;
|
||||
stack.ss_sp = PyMem_Malloc(stack.ss_size);
|
||||
if (stack.ss_sp != NULL) {
|
||||
err = sigaltstack(&stack, NULL);
|
||||
if (err) {
|
||||
PyMem_Free(stack.ss_sp);
|
||||
stack.ss_sp = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
thread.running = 0;
|
||||
thread.file = NULL;
|
||||
thread.cancel_event = PyThread_allocate_lock();
|
||||
thread.join_event = PyThread_allocate_lock();
|
||||
if (!thread.cancel_event || !thread.join_event) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"could not allocate locks for faulthandler");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return faulthandler_env_options();
|
||||
}
|
||||
|
||||
void _PyFaulthandler_Fini(void)
|
||||
{
|
||||
#ifdef FAULTHANDLER_USER
|
||||
unsigned int i;
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
/* later */
|
||||
faulthandler_cancel_dump_tracebacks_later();
|
||||
if (thread.cancel_event) {
|
||||
PyThread_free_lock(thread.cancel_event);
|
||||
thread.cancel_event = NULL;
|
||||
}
|
||||
if (thread.join_event) {
|
||||
PyThread_free_lock(thread.join_event);
|
||||
thread.join_event = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
/* user */
|
||||
if (user_signals != NULL) {
|
||||
for (i=0; i < NSIG; i++)
|
||||
faulthandler_unregister(&user_signals[i], i+1);
|
||||
free(user_signals);
|
||||
user_signals = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* fatal */
|
||||
faulthandler_disable();
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
if (stack.ss_sp != NULL) {
|
||||
PyMem_Free(stack.ss_sp);
|
||||
stack.ss_sp = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
#include "Python.h"
|
||||
|
||||
/* This module is a stripped down version of _bytesio.c with a Py_UNICODE
|
||||
buffer. Most of the functionality is provided by subclassing _StringIO. */
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Py_UNICODE *buf;
|
||||
Py_ssize_t pos;
|
||||
Py_ssize_t string_size;
|
||||
size_t buf_size;
|
||||
} StringIOObject;
|
||||
|
||||
|
||||
/* Internal routine for changing the size, in terms of characters, of the
|
||||
buffer of StringIO objects. The caller should ensure that the 'size'
|
||||
argument is non-negative. Returns 0 on success, -1 otherwise. */
|
||||
static int
|
||||
resize_buffer(StringIOObject *self, size_t size)
|
||||
{
|
||||
/* Here, unsigned types are used to avoid dealing with signed integer
|
||||
overflow, which is undefined in C. */
|
||||
size_t alloc = self->buf_size;
|
||||
Py_UNICODE *new_buf = NULL;
|
||||
|
||||
assert(self->buf != NULL);
|
||||
|
||||
/* For simplicity, stay in the range of the signed type. Anyway, Python
|
||||
doesn't allow strings to be longer than this. */
|
||||
if (size > PY_SSIZE_T_MAX)
|
||||
goto overflow;
|
||||
|
||||
if (size < alloc / 2) {
|
||||
/* Major downsize; resize down to exact size. */
|
||||
alloc = size + 1;
|
||||
}
|
||||
else if (size < alloc) {
|
||||
/* Within allocated size; quick exit */
|
||||
return 0;
|
||||
}
|
||||
else if (size <= alloc * 1.125) {
|
||||
/* Moderate upsize; overallocate similar to list_resize() */
|
||||
alloc = size + (size >> 3) + (size < 9 ? 3 : 6);
|
||||
}
|
||||
else {
|
||||
/* Major upsize; resize up to exact size */
|
||||
alloc = size + 1;
|
||||
}
|
||||
|
||||
if (alloc > ((size_t)-1) / sizeof(Py_UNICODE))
|
||||
goto overflow;
|
||||
new_buf = (Py_UNICODE *)PyMem_Realloc(self->buf,
|
||||
alloc * sizeof(Py_UNICODE));
|
||||
if (new_buf == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return -1;
|
||||
}
|
||||
self->buf_size = alloc;
|
||||
self->buf = new_buf;
|
||||
|
||||
return 0;
|
||||
|
||||
overflow:
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"new buffer size too large");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Internal routine for writing a string of characters to the buffer of a
|
||||
StringIO object. Returns the number of bytes wrote, or -1 on error. */
|
||||
static Py_ssize_t
|
||||
write_str(StringIOObject *self, const Py_UNICODE *str, Py_ssize_t len)
|
||||
{
|
||||
assert(self->buf != NULL);
|
||||
assert(self->pos >= 0);
|
||||
assert(len >= 0);
|
||||
|
||||
/* This overflow check is not strictly necessary. However, it avoids us to
|
||||
deal with funky things like comparing an unsigned and a signed
|
||||
integer. */
|
||||
if (self->pos > PY_SSIZE_T_MAX - len) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"new position too large");
|
||||
return -1;
|
||||
}
|
||||
if (self->pos + len > self->string_size) {
|
||||
if (resize_buffer(self, self->pos + len) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (self->pos > self->string_size) {
|
||||
/* In case of overseek, pad with null bytes the buffer region between
|
||||
the end of stream and the current position.
|
||||
|
||||
0 lo string_size hi
|
||||
| |<---used--->|<----------available----------->|
|
||||
| | <--to pad-->|<---to write---> |
|
||||
0 buf positon
|
||||
|
||||
*/
|
||||
memset(self->buf + self->string_size, '\0',
|
||||
(self->pos - self->string_size) * sizeof(Py_UNICODE));
|
||||
}
|
||||
|
||||
/* Copy the data to the internal buffer, overwriting some of the
|
||||
existing data if self->pos < self->string_size. */
|
||||
memcpy(self->buf + self->pos, str, len * sizeof(Py_UNICODE));
|
||||
self->pos += len;
|
||||
|
||||
/* Set the new length of the internal string if it has changed */
|
||||
if (self->string_size < self->pos) {
|
||||
self->string_size = self->pos;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_getvalue(StringIOObject *self)
|
||||
{
|
||||
return PyUnicode_FromUnicode(self->buf, self->string_size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_tell(StringIOObject *self)
|
||||
{
|
||||
return PyLong_FromSsize_t(self->pos);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_read(StringIOObject *self, PyObject *args)
|
||||
{
|
||||
Py_ssize_t size, n;
|
||||
Py_UNICODE *output;
|
||||
PyObject *arg = Py_None;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:read", &arg))
|
||||
return NULL;
|
||||
|
||||
if (PyLong_Check(arg)) {
|
||||
size = PyLong_AsSsize_t(arg);
|
||||
}
|
||||
else if (arg == Py_None) {
|
||||
/* Read until EOF is reached, by default. */
|
||||
size = -1;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'",
|
||||
Py_TYPE(arg)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* adjust invalid sizes */
|
||||
n = self->string_size - self->pos;
|
||||
if (size < 0 || size > n) {
|
||||
size = n;
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
}
|
||||
|
||||
assert(self->buf != NULL);
|
||||
output = self->buf + self->pos;
|
||||
self->pos += size;
|
||||
|
||||
return PyUnicode_FromUnicode(output, size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_truncate(StringIOObject *self, PyObject *args)
|
||||
{
|
||||
Py_ssize_t size;
|
||||
PyObject *arg = Py_None;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:truncate", &arg))
|
||||
return NULL;
|
||||
|
||||
if (PyLong_Check(arg)) {
|
||||
size = PyLong_AsSsize_t(arg);
|
||||
if (size == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
if (size == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
}
|
||||
else if (arg == Py_None) {
|
||||
/* Truncate to current position if no argument is passed. */
|
||||
size = self->pos;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'",
|
||||
Py_TYPE(arg)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Negative size value %zd", size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size < self->string_size) {
|
||||
self->string_size = size;
|
||||
if (resize_buffer(self, size) < 0)
|
||||
return NULL;
|
||||
}
|
||||
self->pos = size;
|
||||
|
||||
return PyLong_FromSsize_t(size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_seek(StringIOObject *self, PyObject *args)
|
||||
{
|
||||
Py_ssize_t pos;
|
||||
int mode = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &mode))
|
||||
return NULL;
|
||||
|
||||
if (mode != 0 && mode != 1 && mode != 2) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Invalid whence (%i, should be 0, 1 or 2)", mode);
|
||||
return NULL;
|
||||
}
|
||||
else if (pos < 0 && mode == 0) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Negative seek position %zd", pos);
|
||||
return NULL;
|
||||
}
|
||||
else if (mode != 0 && pos != 0) {
|
||||
PyErr_SetString(PyExc_IOError,
|
||||
"Can't do nonzero cur-relative seeks");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mode 0: offset relative to beginning of the string.
|
||||
mode 1: no change to current position.
|
||||
mode 2: change position to end of file. */
|
||||
if (mode == 1) {
|
||||
pos = self->pos;
|
||||
}
|
||||
else if (mode == 2) {
|
||||
pos = self->string_size;
|
||||
}
|
||||
|
||||
self->pos = pos;
|
||||
|
||||
return PyLong_FromSsize_t(self->pos);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_write(StringIOObject *self, PyObject *obj)
|
||||
{
|
||||
const Py_UNICODE *str;
|
||||
Py_ssize_t size;
|
||||
Py_ssize_t n = 0;
|
||||
|
||||
if (PyUnicode_Check(obj)) {
|
||||
str = PyUnicode_AsUnicode(obj);
|
||||
size = PyUnicode_GetSize(obj);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError, "string argument expected, got '%s'",
|
||||
Py_TYPE(obj)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
n = write_str(self, str, size);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyLong_FromSsize_t(n);
|
||||
}
|
||||
|
||||
static void
|
||||
stringio_dealloc(StringIOObject *self)
|
||||
{
|
||||
PyMem_Free(self->buf);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
StringIOObject *self;
|
||||
|
||||
assert(type != NULL && type->tp_alloc != NULL);
|
||||
self = (StringIOObject *)type->tp_alloc(type, 0);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
self->string_size = 0;
|
||||
self->pos = 0;
|
||||
self->buf_size = 0;
|
||||
self->buf = (Py_UNICODE *)PyMem_Malloc(0);
|
||||
if (self->buf == NULL) {
|
||||
Py_DECREF(self);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
static struct PyMethodDef stringio_methods[] = {
|
||||
{"getvalue", (PyCFunction)stringio_getvalue, METH_VARARGS, NULL},
|
||||
{"read", (PyCFunction)stringio_read, METH_VARARGS, NULL},
|
||||
{"tell", (PyCFunction)stringio_tell, METH_NOARGS, NULL},
|
||||
{"truncate", (PyCFunction)stringio_truncate, METH_VARARGS, NULL},
|
||||
{"seek", (PyCFunction)stringio_seek, METH_VARARGS, NULL},
|
||||
{"write", (PyCFunction)stringio_write, METH_O, NULL},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject StringIO_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_stringio._StringIO", /*tp_name*/
|
||||
sizeof(StringIOObject), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
(destructor)stringio_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash*/
|
||||
0, /*tp_call*/
|
||||
0, /*tp_str*/
|
||||
0, /*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
||||
0, /*tp_doc*/
|
||||
0, /*tp_traverse*/
|
||||
0, /*tp_clear*/
|
||||
0, /*tp_richcompare*/
|
||||
0, /*tp_weaklistoffset*/
|
||||
0, /*tp_iter*/
|
||||
0, /*tp_iternext*/
|
||||
stringio_methods, /*tp_methods*/
|
||||
0, /*tp_members*/
|
||||
0, /*tp_getset*/
|
||||
0, /*tp_base*/
|
||||
0, /*tp_dict*/
|
||||
0, /*tp_descr_get*/
|
||||
0, /*tp_descr_set*/
|
||||
0, /*tp_dictoffset*/
|
||||
0, /*tp_init*/
|
||||
0, /*tp_alloc*/
|
||||
stringio_new, /*tp_new*/
|
||||
};
|
||||
|
||||
static struct PyModuleDef _stringiomodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_stringio",
|
||||
NULL,
|
||||
-1,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__stringio(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
if (PyType_Ready(&StringIO_Type) < 0)
|
||||
return NULL;
|
||||
m = PyModule_Create(&_stringiomodule);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
Py_INCREF(&StringIO_Type);
|
||||
if (PyModule_AddObject(m, "_StringIO", (PyObject *)&StringIO_Type) < 0)
|
||||
return NULL;
|
||||
return m;
|
||||
}
|
||||
@@ -0,0 +1,947 @@
|
||||
|
||||
/* Signal module -- many thanks to Lance Ellinghaus */
|
||||
|
||||
/* XXX Signals should be recorded per thread, now we have thread state. */
|
||||
|
||||
#include "Python.h"
|
||||
#include "intrcheck.h"
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifndef SIG_ERR
|
||||
#define SIG_ERR ((PyOS_sighandler_t)(-1))
|
||||
#endif
|
||||
|
||||
#if defined(PYOS_OS2) && !defined(PYCC_GCC)
|
||||
#define NSIG 12
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#ifndef NSIG
|
||||
# if defined(_NSIG)
|
||||
# define NSIG _NSIG /* For BSD/SysV */
|
||||
# elif defined(_SIGMAX)
|
||||
# define NSIG (_SIGMAX + 1) /* For QNX */
|
||||
# elif defined(SIGMAX)
|
||||
# define NSIG (SIGMAX + 1) /* For djgpp */
|
||||
# else
|
||||
# define NSIG 64 /* Use a reasonable default value */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
NOTES ON THE INTERACTION BETWEEN SIGNALS AND THREADS
|
||||
|
||||
When threads are supported, we want the following semantics:
|
||||
|
||||
- only the main thread can set a signal handler
|
||||
- any thread can get a signal handler
|
||||
- signals are only delivered to the main thread
|
||||
|
||||
I.e. we don't support "synchronous signals" like SIGFPE (catching
|
||||
this doesn't make much sense in Python anyway) nor do we support
|
||||
signals as a means of inter-thread communication, since not all
|
||||
thread implementations support that (at least our thread library
|
||||
doesn't).
|
||||
|
||||
We still have the problem that in some implementations signals
|
||||
generated by the keyboard (e.g. SIGINT) are delivered to all
|
||||
threads (e.g. SGI), while in others (e.g. Solaris) such signals are
|
||||
delivered to one random thread (an intermediate possibility would
|
||||
be to deliver it to the main thread -- POSIX?). For now, we have
|
||||
a working implementation that works in all three cases -- the
|
||||
handler ignores signals if getpid() isn't the same as in the main
|
||||
thread. XXX This is a hack.
|
||||
|
||||
GNU pth is a user-space threading library, and as such, all threads
|
||||
run within the same process. In this case, if the currently running
|
||||
thread is not the main_thread, send the signal to the main_thread.
|
||||
*/
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
#include <sys/types.h> /* For pid_t */
|
||||
#include "pythread.h"
|
||||
static long main_thread;
|
||||
static pid_t main_pid;
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
int tripped;
|
||||
PyObject *func;
|
||||
} Handlers[NSIG];
|
||||
|
||||
static sig_atomic_t wakeup_fd = -1;
|
||||
|
||||
/* Speed up sigcheck() when none tripped */
|
||||
static volatile sig_atomic_t is_tripped = 0;
|
||||
|
||||
static PyObject *DefaultHandler;
|
||||
static PyObject *IgnoreHandler;
|
||||
static PyObject *IntHandler;
|
||||
|
||||
/* On Solaris 8, gcc will produce a warning that the function
|
||||
declaration is not a prototype. This is caused by the definition of
|
||||
SIG_DFL as (void (*)())0; the correct declaration would have been
|
||||
(void (*)(int))0. */
|
||||
|
||||
static PyOS_sighandler_t old_siginthandler = SIG_DFL;
|
||||
|
||||
#ifdef HAVE_GETITIMER
|
||||
static PyObject *ItimerError;
|
||||
|
||||
/* auxiliary functions for setitimer/getitimer */
|
||||
static void
|
||||
timeval_from_double(double d, struct timeval *tv)
|
||||
{
|
||||
tv->tv_sec = floor(d);
|
||||
tv->tv_usec = fmod(d, 1.0) * 1000000.0;
|
||||
}
|
||||
|
||||
Py_LOCAL_INLINE(double)
|
||||
double_from_timeval(struct timeval *tv)
|
||||
{
|
||||
return tv->tv_sec + (double)(tv->tv_usec / 1000000.0);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
itimer_retval(struct itimerval *iv)
|
||||
{
|
||||
PyObject *r, *v;
|
||||
|
||||
r = PyTuple_New(2);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) {
|
||||
Py_DECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(r, 0, v);
|
||||
|
||||
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) {
|
||||
Py_DECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(r, 1, v);
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
signal_default_int_handler(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyErr_SetNone(PyExc_KeyboardInterrupt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(default_int_handler_doc,
|
||||
"default_int_handler(...)\n\
|
||||
\n\
|
||||
The default handler for SIGINT installed by Python.\n\
|
||||
It raises KeyboardInterrupt.");
|
||||
|
||||
|
||||
static int
|
||||
checksignals_witharg(void * unused)
|
||||
{
|
||||
return PyErr_CheckSignals();
|
||||
}
|
||||
|
||||
static void
|
||||
signal_handler(int sig_num)
|
||||
{
|
||||
#ifdef WITH_THREAD
|
||||
#ifdef WITH_PTH
|
||||
if (PyThread_get_thread_ident() != main_thread) {
|
||||
pth_raise(*(pth_t *) main_thread, sig_num);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* See NOTES section above */
|
||||
if (getpid() == main_pid) {
|
||||
#endif
|
||||
Handlers[sig_num].tripped = 1;
|
||||
/* Set is_tripped after setting .tripped, as it gets
|
||||
cleared in PyErr_CheckSignals() before .tripped. */
|
||||
is_tripped = 1;
|
||||
Py_AddPendingCall(checksignals_witharg, NULL);
|
||||
if (wakeup_fd != -1)
|
||||
write(wakeup_fd, "\0", 1);
|
||||
#ifdef WITH_THREAD
|
||||
}
|
||||
#endif
|
||||
#ifdef SIGCHLD
|
||||
if (sig_num == SIGCHLD) {
|
||||
/* To avoid infinite recursion, this signal remains
|
||||
reset until explicit re-instated.
|
||||
Don't clear the 'func' field as it is our pointer
|
||||
to the Python handler... */
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
PyOS_setsig(sig_num, signal_handler);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_ALARM
|
||||
static PyObject *
|
||||
signal_alarm(PyObject *self, PyObject *args)
|
||||
{
|
||||
int t;
|
||||
if (!PyArg_ParseTuple(args, "i:alarm", &t))
|
||||
return NULL;
|
||||
/* alarm() returns the number of seconds remaining */
|
||||
return PyLong_FromLong((long)alarm(t));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(alarm_doc,
|
||||
"alarm(seconds)\n\
|
||||
\n\
|
||||
Arrange for SIGALRM to arrive after the given number of seconds.");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PAUSE
|
||||
static PyObject *
|
||||
signal_pause(PyObject *self)
|
||||
{
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
(void)pause();
|
||||
Py_END_ALLOW_THREADS
|
||||
/* make sure that any exceptions that got raised are propagated
|
||||
* back into Python
|
||||
*/
|
||||
if (PyErr_CheckSignals())
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
PyDoc_STRVAR(pause_doc,
|
||||
"pause()\n\
|
||||
\n\
|
||||
Wait until a signal arrives.");
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static PyObject *
|
||||
signal_signal(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *obj;
|
||||
int sig_num;
|
||||
PyObject *old_handler;
|
||||
void (*func)(int);
|
||||
if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj))
|
||||
return NULL;
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal only works in main thread");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
if (sig_num < 1 || sig_num >= NSIG) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
if (obj == IgnoreHandler)
|
||||
func = SIG_IGN;
|
||||
else if (obj == DefaultHandler)
|
||||
func = SIG_DFL;
|
||||
else if (!PyCallable_Check(obj)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
func = signal_handler;
|
||||
if (PyOS_setsig(sig_num, func) == SIG_ERR) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
return NULL;
|
||||
}
|
||||
old_handler = Handlers[sig_num].func;
|
||||
Handlers[sig_num].tripped = 0;
|
||||
Py_INCREF(obj);
|
||||
Handlers[sig_num].func = obj;
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(signal_doc,
|
||||
"signal(sig, action) -> action\n\
|
||||
\n\
|
||||
Set the action for the given signal. The action can be SIG_DFL,\n\
|
||||
SIG_IGN, or a callable Python object. The previous action is\n\
|
||||
returned. See getsignal() for possible return values.\n\
|
||||
\n\
|
||||
*** IMPORTANT NOTICE ***\n\
|
||||
A signal handler function is called with two arguments:\n\
|
||||
the first is the signal number, the second is the interrupted stack frame.");
|
||||
|
||||
|
||||
static PyObject *
|
||||
signal_getsignal(PyObject *self, PyObject *args)
|
||||
{
|
||||
int sig_num;
|
||||
PyObject *old_handler;
|
||||
if (!PyArg_ParseTuple(args, "i:getsignal", &sig_num))
|
||||
return NULL;
|
||||
if (sig_num < 1 || sig_num >= NSIG) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
old_handler = Handlers[sig_num].func;
|
||||
Py_INCREF(old_handler);
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(getsignal_doc,
|
||||
"getsignal(sig) -> action\n\
|
||||
\n\
|
||||
Return the current action for the given signal. The return value can be:\n\
|
||||
SIG_IGN -- if the signal is being ignored\n\
|
||||
SIG_DFL -- if the default action for the signal is in effect\n\
|
||||
None -- if an unknown handler is in effect\n\
|
||||
anything else -- the callable Python object used as a handler");
|
||||
|
||||
#ifdef HAVE_SIGINTERRUPT
|
||||
PyDoc_STRVAR(siginterrupt_doc,
|
||||
"siginterrupt(sig, flag) -> None\n\
|
||||
change system call restart behaviour: if flag is False, system calls\n\
|
||||
will be restarted when interrupted by signal sig, else system calls\n\
|
||||
will be interrupted.");
|
||||
|
||||
static PyObject *
|
||||
signal_siginterrupt(PyObject *self, PyObject *args)
|
||||
{
|
||||
int sig_num;
|
||||
int flag;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ii:siginterrupt", &sig_num, &flag))
|
||||
return NULL;
|
||||
if (sig_num < 1 || sig_num >= NSIG) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
if (siginterrupt(sig_num, flag)<0) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
signal_set_wakeup_fd(PyObject *self, PyObject *args)
|
||||
{
|
||||
struct stat buf;
|
||||
int fd, old_fd;
|
||||
if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd))
|
||||
return NULL;
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"set_wakeup_fd only works in main thread");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
if (fd != -1 && fstat(fd, &buf) != 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "invalid fd");
|
||||
return NULL;
|
||||
}
|
||||
old_fd = wakeup_fd;
|
||||
wakeup_fd = fd;
|
||||
return PyLong_FromLong(old_fd);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(set_wakeup_fd_doc,
|
||||
"set_wakeup_fd(fd) -> fd\n\
|
||||
\n\
|
||||
Sets the fd to be written to (with '\\0') when a signal\n\
|
||||
comes in. A library can use this to wakeup select or poll.\n\
|
||||
The previous fd is returned.\n\
|
||||
\n\
|
||||
The fd must be non-blocking.");
|
||||
|
||||
/* C API for the same, without all the error checking */
|
||||
int
|
||||
PySignal_SetWakeupFd(int fd)
|
||||
{
|
||||
int old_fd = wakeup_fd;
|
||||
if (fd < 0)
|
||||
fd = -1;
|
||||
wakeup_fd = fd;
|
||||
return old_fd;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SETITIMER
|
||||
static PyObject *
|
||||
signal_setitimer(PyObject *self, PyObject *args)
|
||||
{
|
||||
double first;
|
||||
double interval = 0;
|
||||
int which;
|
||||
struct itimerval new, old;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval))
|
||||
return NULL;
|
||||
|
||||
timeval_from_double(first, &new.it_value);
|
||||
timeval_from_double(interval, &new.it_interval);
|
||||
/* Let OS check "which" value */
|
||||
if (setitimer(which, &new, &old) != 0) {
|
||||
PyErr_SetFromErrno(ItimerError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return itimer_retval(&old);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(setitimer_doc,
|
||||
"setitimer(which, seconds[, interval])\n\
|
||||
\n\
|
||||
Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\
|
||||
or ITIMER_PROF) to fire after value seconds and after\n\
|
||||
that every interval seconds.\n\
|
||||
The itimer can be cleared by setting seconds to zero.\n\
|
||||
\n\
|
||||
Returns old values as a tuple: (delay, interval).");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_GETITIMER
|
||||
static PyObject *
|
||||
signal_getitimer(PyObject *self, PyObject *args)
|
||||
{
|
||||
int which;
|
||||
struct itimerval old;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i:getitimer", &which))
|
||||
return NULL;
|
||||
|
||||
if (getitimer(which, &old) != 0) {
|
||||
PyErr_SetFromErrno(ItimerError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return itimer_retval(&old);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(getitimer_doc,
|
||||
"getitimer(which)\n\
|
||||
\n\
|
||||
Returns current value of given itimer.");
|
||||
#endif
|
||||
|
||||
|
||||
/* List of functions defined in the module */
|
||||
static PyMethodDef signal_methods[] = {
|
||||
#ifdef HAVE_ALARM
|
||||
{"alarm", signal_alarm, METH_VARARGS, alarm_doc},
|
||||
#endif
|
||||
#ifdef HAVE_SETITIMER
|
||||
{"setitimer", signal_setitimer, METH_VARARGS, setitimer_doc},
|
||||
#endif
|
||||
#ifdef HAVE_GETITIMER
|
||||
{"getitimer", signal_getitimer, METH_VARARGS, getitimer_doc},
|
||||
#endif
|
||||
{"signal", signal_signal, METH_VARARGS, signal_doc},
|
||||
{"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc},
|
||||
{"set_wakeup_fd", signal_set_wakeup_fd, METH_VARARGS, set_wakeup_fd_doc},
|
||||
#ifdef HAVE_SIGINTERRUPT
|
||||
{"siginterrupt", signal_siginterrupt, METH_VARARGS, siginterrupt_doc},
|
||||
#endif
|
||||
#ifdef HAVE_PAUSE
|
||||
{"pause", (PyCFunction)signal_pause,
|
||||
METH_NOARGS,pause_doc},
|
||||
#endif
|
||||
{"default_int_handler", signal_default_int_handler,
|
||||
METH_VARARGS, default_int_handler_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
||||
PyDoc_STRVAR(module_doc,
|
||||
"This module provides mechanisms to use signal handlers in Python.\n\
|
||||
\n\
|
||||
Functions:\n\
|
||||
\n\
|
||||
alarm() -- cause SIGALRM after a specified time [Unix only]\n\
|
||||
setitimer() -- cause a signal (described below) after a specified\n\
|
||||
float time and the timer may restart then [Unix only]\n\
|
||||
getitimer() -- get current value of timer [Unix only]\n\
|
||||
signal() -- set the action for a given signal\n\
|
||||
getsignal() -- get the signal action for a given signal\n\
|
||||
pause() -- wait until a signal arrives [Unix only]\n\
|
||||
default_int_handler() -- default SIGINT handler\n\
|
||||
\n\
|
||||
signal constants:\n\
|
||||
SIG_DFL -- used to refer to the system default handler\n\
|
||||
SIG_IGN -- used to ignore the signal\n\
|
||||
NSIG -- number of defined signals\n\
|
||||
SIGINT, SIGTERM, etc. -- signal numbers\n\
|
||||
\n\
|
||||
itimer constants:\n\
|
||||
ITIMER_REAL -- decrements in real time, and delivers SIGALRM upon\n\
|
||||
expiration\n\
|
||||
ITIMER_VIRTUAL -- decrements only when the process is executing,\n\
|
||||
and delivers SIGVTALRM upon expiration\n\
|
||||
ITIMER_PROF -- decrements both when the process is executing and\n\
|
||||
when the system is executing on behalf of the process.\n\
|
||||
Coupled with ITIMER_VIRTUAL, this timer is usually\n\
|
||||
used to profile the time spent by the application\n\
|
||||
in user and kernel space. SIGPROF is delivered upon\n\
|
||||
expiration.\n\
|
||||
\n\n\
|
||||
*** IMPORTANT NOTICE ***\n\
|
||||
A signal handler function is called with two arguments:\n\
|
||||
the first is the signal number, the second is the interrupted stack frame.");
|
||||
|
||||
static struct PyModuleDef signalmodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"signal",
|
||||
module_doc,
|
||||
-1,
|
||||
signal_methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_signal(void)
|
||||
{
|
||||
PyObject *m, *d, *x;
|
||||
int i;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
main_thread = PyThread_get_thread_ident();
|
||||
main_pid = getpid();
|
||||
#endif
|
||||
|
||||
/* Create the module and add the functions */
|
||||
m = PyModule_Create(&signalmodule);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Add some symbolic constants to the module */
|
||||
d = PyModule_GetDict(m);
|
||||
|
||||
x = DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
|
||||
if (!x || PyDict_SetItemString(d, "SIG_DFL", x) < 0)
|
||||
goto finally;
|
||||
|
||||
x = IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN);
|
||||
if (!x || PyDict_SetItemString(d, "SIG_IGN", x) < 0)
|
||||
goto finally;
|
||||
|
||||
x = PyLong_FromLong((long)NSIG);
|
||||
if (!x || PyDict_SetItemString(d, "NSIG", x) < 0)
|
||||
goto finally;
|
||||
Py_DECREF(x);
|
||||
|
||||
x = IntHandler = PyDict_GetItemString(d, "default_int_handler");
|
||||
if (!x)
|
||||
goto finally;
|
||||
Py_INCREF(IntHandler);
|
||||
|
||||
Handlers[0].tripped = 0;
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
void (*t)(int);
|
||||
t = PyOS_getsig(i);
|
||||
Handlers[i].tripped = 0;
|
||||
if (t == SIG_DFL)
|
||||
Handlers[i].func = DefaultHandler;
|
||||
else if (t == SIG_IGN)
|
||||
Handlers[i].func = IgnoreHandler;
|
||||
else
|
||||
Handlers[i].func = Py_None; /* None of our business */
|
||||
Py_INCREF(Handlers[i].func);
|
||||
}
|
||||
if (Handlers[SIGINT].func == DefaultHandler) {
|
||||
/* Install default int handler */
|
||||
Py_INCREF(IntHandler);
|
||||
Py_DECREF(Handlers[SIGINT].func);
|
||||
Handlers[SIGINT].func = IntHandler;
|
||||
old_siginthandler = PyOS_setsig(SIGINT, signal_handler);
|
||||
}
|
||||
|
||||
#ifdef SIGHUP
|
||||
x = PyLong_FromLong(SIGHUP);
|
||||
PyDict_SetItemString(d, "SIGHUP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
x = PyLong_FromLong(SIGINT);
|
||||
PyDict_SetItemString(d, "SIGINT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGBREAK
|
||||
x = PyLong_FromLong(SIGBREAK);
|
||||
PyDict_SetItemString(d, "SIGBREAK", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGQUIT
|
||||
x = PyLong_FromLong(SIGQUIT);
|
||||
PyDict_SetItemString(d, "SIGQUIT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGILL
|
||||
x = PyLong_FromLong(SIGILL);
|
||||
PyDict_SetItemString(d, "SIGILL", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTRAP
|
||||
x = PyLong_FromLong(SIGTRAP);
|
||||
PyDict_SetItemString(d, "SIGTRAP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGIOT
|
||||
x = PyLong_FromLong(SIGIOT);
|
||||
PyDict_SetItemString(d, "SIGIOT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGABRT
|
||||
x = PyLong_FromLong(SIGABRT);
|
||||
PyDict_SetItemString(d, "SIGABRT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGEMT
|
||||
x = PyLong_FromLong(SIGEMT);
|
||||
PyDict_SetItemString(d, "SIGEMT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGFPE
|
||||
x = PyLong_FromLong(SIGFPE);
|
||||
PyDict_SetItemString(d, "SIGFPE", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGKILL
|
||||
x = PyLong_FromLong(SIGKILL);
|
||||
PyDict_SetItemString(d, "SIGKILL", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGBUS
|
||||
x = PyLong_FromLong(SIGBUS);
|
||||
PyDict_SetItemString(d, "SIGBUS", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGSEGV
|
||||
x = PyLong_FromLong(SIGSEGV);
|
||||
PyDict_SetItemString(d, "SIGSEGV", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGSYS
|
||||
x = PyLong_FromLong(SIGSYS);
|
||||
PyDict_SetItemString(d, "SIGSYS", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
x = PyLong_FromLong(SIGPIPE);
|
||||
PyDict_SetItemString(d, "SIGPIPE", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGALRM
|
||||
x = PyLong_FromLong(SIGALRM);
|
||||
PyDict_SetItemString(d, "SIGALRM", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTERM
|
||||
x = PyLong_FromLong(SIGTERM);
|
||||
PyDict_SetItemString(d, "SIGTERM", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGUSR1
|
||||
x = PyLong_FromLong(SIGUSR1);
|
||||
PyDict_SetItemString(d, "SIGUSR1", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGUSR2
|
||||
x = PyLong_FromLong(SIGUSR2);
|
||||
PyDict_SetItemString(d, "SIGUSR2", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGCLD
|
||||
x = PyLong_FromLong(SIGCLD);
|
||||
PyDict_SetItemString(d, "SIGCLD", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGCHLD
|
||||
x = PyLong_FromLong(SIGCHLD);
|
||||
PyDict_SetItemString(d, "SIGCHLD", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPWR
|
||||
x = PyLong_FromLong(SIGPWR);
|
||||
PyDict_SetItemString(d, "SIGPWR", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGIO
|
||||
x = PyLong_FromLong(SIGIO);
|
||||
PyDict_SetItemString(d, "SIGIO", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGURG
|
||||
x = PyLong_FromLong(SIGURG);
|
||||
PyDict_SetItemString(d, "SIGURG", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGWINCH
|
||||
x = PyLong_FromLong(SIGWINCH);
|
||||
PyDict_SetItemString(d, "SIGWINCH", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPOLL
|
||||
x = PyLong_FromLong(SIGPOLL);
|
||||
PyDict_SetItemString(d, "SIGPOLL", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGSTOP
|
||||
x = PyLong_FromLong(SIGSTOP);
|
||||
PyDict_SetItemString(d, "SIGSTOP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTSTP
|
||||
x = PyLong_FromLong(SIGTSTP);
|
||||
PyDict_SetItemString(d, "SIGTSTP", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGCONT
|
||||
x = PyLong_FromLong(SIGCONT);
|
||||
PyDict_SetItemString(d, "SIGCONT", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTTIN
|
||||
x = PyLong_FromLong(SIGTTIN);
|
||||
PyDict_SetItemString(d, "SIGTTIN", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGTTOU
|
||||
x = PyLong_FromLong(SIGTTOU);
|
||||
PyDict_SetItemString(d, "SIGTTOU", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGVTALRM
|
||||
x = PyLong_FromLong(SIGVTALRM);
|
||||
PyDict_SetItemString(d, "SIGVTALRM", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGPROF
|
||||
x = PyLong_FromLong(SIGPROF);
|
||||
PyDict_SetItemString(d, "SIGPROF", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGXCPU
|
||||
x = PyLong_FromLong(SIGXCPU);
|
||||
PyDict_SetItemString(d, "SIGXCPU", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGXFSZ
|
||||
x = PyLong_FromLong(SIGXFSZ);
|
||||
PyDict_SetItemString(d, "SIGXFSZ", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGRTMIN
|
||||
x = PyLong_FromLong(SIGRTMIN);
|
||||
PyDict_SetItemString(d, "SIGRTMIN", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGRTMAX
|
||||
x = PyLong_FromLong(SIGRTMAX);
|
||||
PyDict_SetItemString(d, "SIGRTMAX", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
#ifdef SIGINFO
|
||||
x = PyLong_FromLong(SIGINFO);
|
||||
PyDict_SetItemString(d, "SIGINFO", x);
|
||||
Py_XDECREF(x);
|
||||
#endif
|
||||
|
||||
#ifdef ITIMER_REAL
|
||||
x = PyLong_FromLong(ITIMER_REAL);
|
||||
PyDict_SetItemString(d, "ITIMER_REAL", x);
|
||||
Py_DECREF(x);
|
||||
#endif
|
||||
#ifdef ITIMER_VIRTUAL
|
||||
x = PyLong_FromLong(ITIMER_VIRTUAL);
|
||||
PyDict_SetItemString(d, "ITIMER_VIRTUAL", x);
|
||||
Py_DECREF(x);
|
||||
#endif
|
||||
#ifdef ITIMER_PROF
|
||||
x = PyLong_FromLong(ITIMER_PROF);
|
||||
PyDict_SetItemString(d, "ITIMER_PROF", x);
|
||||
Py_DECREF(x);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_SETITIMER) || defined (HAVE_GETITIMER)
|
||||
ItimerError = PyErr_NewException("signal.ItimerError",
|
||||
PyExc_IOError, NULL);
|
||||
if (ItimerError != NULL)
|
||||
PyDict_SetItemString(d, "ItimerError", ItimerError);
|
||||
#endif
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
Py_DECREF(m);
|
||||
m = NULL;
|
||||
}
|
||||
|
||||
finally:
|
||||
return m;
|
||||
}
|
||||
|
||||
static void
|
||||
finisignal(void)
|
||||
{
|
||||
int i;
|
||||
PyObject *func;
|
||||
|
||||
PyOS_setsig(SIGINT, old_siginthandler);
|
||||
old_siginthandler = SIG_DFL;
|
||||
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
func = Handlers[i].func;
|
||||
Handlers[i].tripped = 0;
|
||||
Handlers[i].func = NULL;
|
||||
if (i != SIGINT && func != NULL && func != Py_None &&
|
||||
func != DefaultHandler && func != IgnoreHandler)
|
||||
PyOS_setsig(i, SIG_DFL);
|
||||
Py_XDECREF(func);
|
||||
}
|
||||
|
||||
Py_XDECREF(IntHandler);
|
||||
IntHandler = NULL;
|
||||
Py_XDECREF(DefaultHandler);
|
||||
DefaultHandler = NULL;
|
||||
Py_XDECREF(IgnoreHandler);
|
||||
IgnoreHandler = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Declared in pyerrors.h */
|
||||
int
|
||||
PyErr_CheckSignals(void)
|
||||
{
|
||||
int i;
|
||||
PyObject *f;
|
||||
|
||||
if (!is_tripped)
|
||||
return 0;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The is_stripped variable is meant to speed up the calls to
|
||||
* PyErr_CheckSignals (both directly or via pending calls) when no
|
||||
* signal has arrived. This variable is set to 1 when a signal arrives
|
||||
* and it is set to 0 here, when we know some signals arrived. This way
|
||||
* we can run the registered handlers with no signals blocked.
|
||||
*
|
||||
* NOTE: with this approach we can have a situation where is_tripped is
|
||||
* 1 but we have no more signals to handle (Handlers[i].tripped
|
||||
* is 0 for every signal i). This won't do us any harm (except
|
||||
* we're gonna spent some cycles for nothing). This happens when
|
||||
* we receive a signal i after we zero is_tripped and before we
|
||||
* check Handlers[i].tripped.
|
||||
*/
|
||||
is_tripped = 0;
|
||||
|
||||
if (!(f = (PyObject *)PyEval_GetFrame()))
|
||||
f = Py_None;
|
||||
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
if (Handlers[i].tripped) {
|
||||
PyObject *result = NULL;
|
||||
PyObject *arglist = Py_BuildValue("(iO)", i, f);
|
||||
Handlers[i].tripped = 0;
|
||||
|
||||
if (arglist) {
|
||||
result = PyEval_CallObject(Handlers[i].func,
|
||||
arglist);
|
||||
Py_DECREF(arglist);
|
||||
}
|
||||
if (!result)
|
||||
return -1;
|
||||
|
||||
Py_DECREF(result);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Replacements for intrcheck.c functionality
|
||||
* Declared in pyerrors.h
|
||||
*/
|
||||
void
|
||||
PyErr_SetInterrupt(void)
|
||||
{
|
||||
is_tripped = 1;
|
||||
Handlers[SIGINT].tripped = 1;
|
||||
Py_AddPendingCall((int (*)(void *))PyErr_CheckSignals, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
PyOS_InitInterrupts(void)
|
||||
{
|
||||
PyObject *m = PyInit_signal();
|
||||
if (m) {
|
||||
_PyImport_FixupExtension(m, "signal", "signal");
|
||||
Py_DECREF(m);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PyOS_FiniInterrupts(void)
|
||||
{
|
||||
finisignal();
|
||||
}
|
||||
|
||||
int
|
||||
PyOS_InterruptOccurred(void)
|
||||
{
|
||||
if (Handlers[SIGINT].tripped) {
|
||||
#ifdef WITH_THREAD
|
||||
if (PyThread_get_thread_ident() != main_thread)
|
||||
return 0;
|
||||
#endif
|
||||
Handlers[SIGINT].tripped = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
PyOS_AfterFork(void)
|
||||
{
|
||||
#ifdef WITH_THREAD
|
||||
PyEval_ReInitThreads();
|
||||
main_thread = PyThread_get_thread_ident();
|
||||
main_pid = getpid();
|
||||
_PyImport_ReInitLock();
|
||||
PyThread_ReInitTLS();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,998 @@
|
||||
#include "Python.h"
|
||||
#include "pythread.h"
|
||||
#include <signal.h>
|
||||
#include <object.h>
|
||||
#include <frameobject.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
# define FAULTHANDLER_LATER
|
||||
#endif
|
||||
|
||||
#ifndef MS_WINDOWS
|
||||
/* register() is useless on Windows, because only SIGSEGV and SIGILL can be
|
||||
handled by the process, and these signals can only be used with enable(),
|
||||
not using register() */
|
||||
# define FAULTHANDLER_USER
|
||||
#endif
|
||||
|
||||
/* Allocate at maximum 100 MB of the stack to raise the stack overflow */
|
||||
#define STACK_OVERFLOW_MAX_SIZE (100*1024*1024)
|
||||
|
||||
#define PUTS(fd, str) write(fd, str, strlen(str))
|
||||
|
||||
#ifdef HAVE_SIGACTION
|
||||
typedef struct sigaction _Py_sighandler_t;
|
||||
#else
|
||||
typedef PyOS_sighandler_t _Py_sighandler_t;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int signum;
|
||||
int enabled;
|
||||
const char* name;
|
||||
_Py_sighandler_t previous;
|
||||
int all_threads;
|
||||
} fault_handler_t;
|
||||
|
||||
static struct {
|
||||
int enabled;
|
||||
PyObject *file;
|
||||
int fd;
|
||||
int all_threads;
|
||||
} fatal_error = {0, NULL, -1, 0};
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
static struct {
|
||||
PyObject *file;
|
||||
int fd;
|
||||
PY_TIMEOUT_T timeout_ms; /* timeout in microseconds */
|
||||
int repeat;
|
||||
volatile int running;
|
||||
PyInterpreterState *interp;
|
||||
int exit;
|
||||
/* released by parent thread when cancel request */
|
||||
PyThread_type_lock cancel_event;
|
||||
/* released by child thread when joined */
|
||||
PyThread_type_lock join_event;
|
||||
} thread;
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
typedef struct {
|
||||
int enabled;
|
||||
PyObject *file;
|
||||
int fd;
|
||||
int all_threads;
|
||||
_Py_sighandler_t previous;
|
||||
} user_signal_t;
|
||||
|
||||
static user_signal_t *user_signals;
|
||||
|
||||
/* the following macros come from Python: Modules/signalmodule.c */
|
||||
#if defined(PYOS_OS2) && !defined(PYCC_GCC)
|
||||
#define NSIG 12
|
||||
#endif
|
||||
#ifndef NSIG
|
||||
# if defined(_NSIG)
|
||||
# define NSIG _NSIG /* For BSD/SysV */
|
||||
# elif defined(_SIGMAX)
|
||||
# define NSIG (_SIGMAX + 1) /* For QNX */
|
||||
# elif defined(SIGMAX)
|
||||
# define NSIG (SIGMAX + 1) /* For djgpp */
|
||||
# else
|
||||
# define NSIG 64 /* Use a reasonable default value */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* FAULTHANDLER_USER */
|
||||
|
||||
|
||||
static fault_handler_t faulthandler_handlers[] = {
|
||||
#ifdef SIGBUS
|
||||
{SIGBUS, 0, "Bus error", },
|
||||
#endif
|
||||
#ifdef SIGILL
|
||||
{SIGILL, 0, "Illegal instruction", },
|
||||
#endif
|
||||
{SIGFPE, 0, "Floating point exception", },
|
||||
/* define SIGSEGV at the end to make it the default choice if searching the
|
||||
handler fails in faulthandler_fatal_error() */
|
||||
{SIGSEGV, 0, "Segmentation fault", }
|
||||
};
|
||||
static const unsigned char faulthandler_nsignals = \
|
||||
sizeof(faulthandler_handlers) / sizeof(faulthandler_handlers[0]);
|
||||
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
static stack_t stack;
|
||||
#endif
|
||||
|
||||
|
||||
/* Get the file descriptor of a file by calling its fileno() method and then
|
||||
call its flush() method.
|
||||
|
||||
If file is NULL or Py_None, use sys.stderr as the new file.
|
||||
|
||||
On success, return the new file and write the file descriptor into *p_fd.
|
||||
On error, return NULL. */
|
||||
|
||||
static PyObject*
|
||||
faulthandler_get_fileno(PyObject *file, int *p_fd)
|
||||
{
|
||||
PyObject *result;
|
||||
long fd_long;
|
||||
int fd;
|
||||
|
||||
if (file == NULL || file == Py_None) {
|
||||
file = PySys_GetObject("stderr");
|
||||
if (file == NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.stderr");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
result = PyObject_CallMethod(file, "fileno", "");
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
fd = -1;
|
||||
if (PyLong_Check(result)) {
|
||||
fd_long = PyLong_AsLong(result);
|
||||
if (0 <= fd_long && fd_long < INT_MAX)
|
||||
fd = (int)fd_long;
|
||||
}
|
||||
Py_DECREF(result);
|
||||
|
||||
if (fd == -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"file.fileno() is not a valid file descriptor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PyObject_CallMethod(file, "flush", "");
|
||||
if (result != NULL)
|
||||
Py_DECREF(result);
|
||||
else {
|
||||
/* ignore flush() error */
|
||||
PyErr_Clear();
|
||||
}
|
||||
*p_fd = fd;
|
||||
return file;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_dump_traceback_py(PyObject *self,
|
||||
PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"file", "all_threads", NULL};
|
||||
PyObject *file = NULL;
|
||||
int all_threads = 0;
|
||||
PyThreadState *tstate;
|
||||
const char *errmsg;
|
||||
int fd;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"|Oi:dump_traceback", kwlist,
|
||||
&file, &all_threads))
|
||||
return NULL;
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
/* The caller holds the GIL and so PyThreadState_Get() can be used */
|
||||
tstate = PyThreadState_Get();
|
||||
if (tstate == NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"unable to get the current thread state");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (all_threads) {
|
||||
errmsg = _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
|
||||
if (errmsg != NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError, errmsg);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_Py_DumpTraceback(fd, tstate);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
/* Handler of SIGSEGV, SIGFPE, SIGBUS and SIGILL signals.
|
||||
|
||||
Display the current Python traceback, restore the previous handler and call
|
||||
the previous handler.
|
||||
|
||||
On Windows, don't call explictly the previous handler, because Windows
|
||||
signal handler would not be called (for an unknown reason). The execution of
|
||||
the program continues at faulthandler_fatal_error() exit, but the same
|
||||
instruction will raise the same fault (signal), and so the previous handler
|
||||
will be called.
|
||||
|
||||
This function is signal safe and should only call signal safe functions. */
|
||||
|
||||
static void
|
||||
faulthandler_fatal_error(
|
||||
int signum
|
||||
#ifdef HAVE_SIGACTION
|
||||
, siginfo_t *siginfo, void *ucontext
|
||||
#endif
|
||||
)
|
||||
{
|
||||
const int fd = fatal_error.fd;
|
||||
unsigned int i;
|
||||
fault_handler_t *handler = NULL;
|
||||
PyThreadState *tstate;
|
||||
|
||||
if (!fatal_error.enabled)
|
||||
return;
|
||||
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
handler = &faulthandler_handlers[i];
|
||||
if (handler->signum == signum)
|
||||
break;
|
||||
}
|
||||
if (handler == NULL) {
|
||||
/* faulthandler_nsignals == 0 (unlikely) */
|
||||
return;
|
||||
}
|
||||
|
||||
/* restore the previous handler */
|
||||
#ifdef HAVE_SIGACTION
|
||||
(void)sigaction(handler->signum, &handler->previous, NULL);
|
||||
#else
|
||||
(void)signal(handler->signum, handler->previous);
|
||||
#endif
|
||||
handler->enabled = 0;
|
||||
|
||||
PUTS(fd, "Fatal Python error: ");
|
||||
PUTS(fd, handler->name);
|
||||
PUTS(fd, "\n\n");
|
||||
|
||||
/* SIGSEGV, SIGFPE, SIGBUS and SIGILL are synchronous signals and so are
|
||||
delivered to the thread that caused the fault. Get the Python thread
|
||||
state of the current thread.
|
||||
|
||||
PyThreadState_Get() doesn't give the state of the thread that caused the
|
||||
fault if the thread released the GIL, and so this function cannot be
|
||||
used. Read the thread local storage (TLS) instead: call
|
||||
PyGILState_GetThisThreadState(). */
|
||||
tstate = PyGILState_GetThisThreadState();
|
||||
if (tstate == NULL)
|
||||
return;
|
||||
|
||||
if (fatal_error.all_threads)
|
||||
_Py_DumpTracebackThreads(fd, tstate->interp, tstate);
|
||||
else
|
||||
_Py_DumpTraceback(fd, tstate);
|
||||
|
||||
#ifndef MS_WINDOWS
|
||||
/* call the previous signal handler: it is called if we use sigaction()
|
||||
thanks to SA_NODEFER flag, otherwise it is deferred */
|
||||
raise(signum);
|
||||
#else
|
||||
/* on Windows, don't call explictly the previous handler, because Windows
|
||||
signal handler would not be called */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Install handler for fatal signals (SIGSEGV, SIGFPE, ...). */
|
||||
|
||||
static PyObject*
|
||||
faulthandler_enable(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"file", "all_threads", NULL};
|
||||
PyObject *file = NULL;
|
||||
int all_threads = 0;
|
||||
unsigned int i;
|
||||
fault_handler_t *handler;
|
||||
#ifdef HAVE_SIGACTION
|
||||
struct sigaction action;
|
||||
#endif
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"|Oi:enable", kwlist, &file, &all_threads))
|
||||
return NULL;
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
Py_XDECREF(fatal_error.file);
|
||||
Py_INCREF(file);
|
||||
fatal_error.file = file;
|
||||
fatal_error.fd = fd;
|
||||
fatal_error.all_threads = all_threads;
|
||||
|
||||
if (!fatal_error.enabled) {
|
||||
fatal_error.enabled = 1;
|
||||
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
handler = &faulthandler_handlers[i];
|
||||
#ifdef HAVE_SIGACTION
|
||||
action.sa_sigaction = faulthandler_fatal_error;
|
||||
sigemptyset(&action.sa_mask);
|
||||
/* Do not prevent the signal from being received from within
|
||||
its own signal handler */
|
||||
action.sa_flags = SA_NODEFER;
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
if (stack.ss_sp != NULL) {
|
||||
/* Call the signal handler on an alternate signal stack
|
||||
provided by sigaltstack() */
|
||||
action.sa_flags |= SA_ONSTACK;
|
||||
}
|
||||
#endif
|
||||
err = sigaction(handler->signum, &action, &handler->previous);
|
||||
#else
|
||||
handler->previous = signal(handler->signum,
|
||||
faulthandler_fatal_error);
|
||||
err = (handler->previous == SIG_ERR);
|
||||
#endif
|
||||
if (err) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
return NULL;
|
||||
}
|
||||
handler->enabled = 1;
|
||||
}
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
faulthandler_disable(void)
|
||||
{
|
||||
unsigned int i;
|
||||
fault_handler_t *handler;
|
||||
|
||||
if (fatal_error.enabled) {
|
||||
fatal_error.enabled = 0;
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
handler = &faulthandler_handlers[i];
|
||||
if (!handler->enabled)
|
||||
continue;
|
||||
#ifdef HAVE_SIGACTION
|
||||
(void)sigaction(handler->signum, &handler->previous, NULL);
|
||||
#else
|
||||
(void)signal(handler->signum, handler->previous);
|
||||
#endif
|
||||
handler->enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Py_CLEAR(fatal_error.file);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_disable_py(PyObject *self)
|
||||
{
|
||||
if (!fatal_error.enabled) {
|
||||
Py_INCREF(Py_False);
|
||||
return Py_False;
|
||||
}
|
||||
faulthandler_disable();
|
||||
Py_INCREF(Py_True);
|
||||
return Py_True;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_is_enabled(PyObject *self)
|
||||
{
|
||||
return PyBool_FromLong(fatal_error.enabled);
|
||||
}
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
|
||||
static void
|
||||
faulthandler_thread(void *unused)
|
||||
{
|
||||
PyLockStatus st;
|
||||
const char* errmsg;
|
||||
PyThreadState *current;
|
||||
int ok;
|
||||
|
||||
do {
|
||||
st = PyThread_acquire_lock_timed(thread.cancel_event,
|
||||
thread.timeout_ms, 0);
|
||||
if (st == PY_LOCK_ACQUIRED) {
|
||||
/* Cancelled by user */
|
||||
PyThread_release_lock(thread.cancel_event);
|
||||
break;
|
||||
}
|
||||
/* Timeout => dump traceback */
|
||||
assert(st == PY_LOCK_FAILURE);
|
||||
|
||||
/* get the thread holding the GIL, NULL if no thread hold the GIL */
|
||||
current = _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
|
||||
errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current);
|
||||
ok = (errmsg == NULL);
|
||||
|
||||
if (thread.exit)
|
||||
_exit(1);
|
||||
} while (ok && thread.repeat);
|
||||
|
||||
/* The only way out */
|
||||
thread.running = 0;
|
||||
PyThread_release_lock(thread.join_event);
|
||||
}
|
||||
|
||||
static void
|
||||
faulthandler_cancel_dump_tracebacks_later(void)
|
||||
{
|
||||
if (thread.running) {
|
||||
/* Notify cancellation */
|
||||
PyThread_release_lock(thread.cancel_event);
|
||||
/* Wait for thread to join */
|
||||
PyThread_acquire_lock(thread.join_event, 1);
|
||||
assert(thread.running == 0);
|
||||
PyThread_release_lock(thread.join_event);
|
||||
}
|
||||
Py_CLEAR(thread.file);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_dump_traceback_later(PyObject *self,
|
||||
PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL};
|
||||
double timeout;
|
||||
PY_TIMEOUT_T timeout_ms;
|
||||
int repeat = 0;
|
||||
PyObject *file = NULL;
|
||||
int fd;
|
||||
int exit = 0;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"d|iOi:dump_tracebacks_later", kwlist,
|
||||
&timeout, &repeat, &file, &exit))
|
||||
return NULL;
|
||||
timeout *= 1e6;
|
||||
if (timeout >= (double) PY_TIMEOUT_MAX) {
|
||||
PyErr_SetString(PyExc_OverflowError, "timeout value is too large");
|
||||
return NULL;
|
||||
}
|
||||
timeout_ms = (PY_TIMEOUT_T)timeout;
|
||||
if (timeout_ms <= 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Cancel previous thread, if running */
|
||||
faulthandler_cancel_dump_tracebacks_later();
|
||||
|
||||
Py_XDECREF(thread.file);
|
||||
Py_INCREF(file);
|
||||
thread.file = file;
|
||||
thread.fd = fd;
|
||||
thread.timeout_ms = timeout_ms;
|
||||
thread.repeat = repeat;
|
||||
thread.interp = PyThreadState_Get()->interp;
|
||||
thread.exit = exit;
|
||||
|
||||
/* Arm these locks to serve as events when released */
|
||||
PyThread_acquire_lock(thread.join_event, 1);
|
||||
PyThread_acquire_lock(thread.cancel_event, 1);
|
||||
|
||||
thread.running = 1;
|
||||
if (PyThread_start_new_thread(faulthandler_thread, NULL) == -1) {
|
||||
thread.running = 0;
|
||||
Py_CLEAR(thread.file);
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"unable to start watchdog thread");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_cancel_dump_tracebacks_later_py(PyObject *self)
|
||||
{
|
||||
faulthandler_cancel_dump_tracebacks_later();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif /* FAULTHANDLER_LATER */
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
/* Handler of user signals (e.g. SIGUSR1).
|
||||
|
||||
Dump the traceback of the current thread, or of all threads if
|
||||
thread.all_threads is true.
|
||||
|
||||
This function is signal safe and should only call signal safe functions. */
|
||||
|
||||
static void
|
||||
faulthandler_user(int signum)
|
||||
{
|
||||
user_signal_t *user;
|
||||
PyThreadState *tstate;
|
||||
|
||||
user = &user_signals[signum];
|
||||
if (!user->enabled)
|
||||
return;
|
||||
|
||||
/* PyThreadState_Get() doesn't give the state of the current thread if
|
||||
the thread doesn't hold the GIL. Read the thread local storage (TLS)
|
||||
instead: call PyGILState_GetThisThreadState(). */
|
||||
tstate = PyGILState_GetThisThreadState();
|
||||
if (tstate == NULL) {
|
||||
/* unable to get the current thread, do nothing */
|
||||
return;
|
||||
}
|
||||
|
||||
if (user->all_threads)
|
||||
_Py_DumpTracebackThreads(user->fd, tstate->interp, tstate);
|
||||
else
|
||||
_Py_DumpTraceback(user->fd, tstate);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_register(PyObject *self,
|
||||
PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"signum", "file", "all_threads", NULL};
|
||||
int signum;
|
||||
PyObject *file = NULL;
|
||||
int all_threads = 0;
|
||||
int fd;
|
||||
unsigned int i;
|
||||
user_signal_t *user;
|
||||
_Py_sighandler_t previous;
|
||||
#ifdef HAVE_SIGACTION
|
||||
struct sigaction action;
|
||||
#endif
|
||||
int err;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"i|Oi:register", kwlist,
|
||||
&signum, &file, &all_threads))
|
||||
return NULL;
|
||||
|
||||
if (signum < 1 || NSIG <= signum) {
|
||||
PyErr_SetString(PyExc_ValueError, "signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i=0; i < faulthandler_nsignals; i++) {
|
||||
if (faulthandler_handlers[i].signum == signum) {
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"signal %i cannot be registered by register(), "
|
||||
"use enable() instead",
|
||||
signum);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
file = faulthandler_get_fileno(file, &fd);
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
if (user_signals == NULL) {
|
||||
user_signals = calloc(NSIG, sizeof(user_signal_t));
|
||||
if (user_signals == NULL)
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
user = &user_signals[signum];
|
||||
|
||||
if (!user->enabled) {
|
||||
#ifdef HAVE_SIGACTION
|
||||
action.sa_handler = faulthandler_user;
|
||||
sigemptyset(&action.sa_mask);
|
||||
/* if the signal is received while the kernel is executing a system
|
||||
call, try to restart the system call instead of interrupting it and
|
||||
return EINTR */
|
||||
action.sa_flags = SA_RESTART;
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
if (stack.ss_sp != NULL) {
|
||||
/* Call the signal handler on an alternate signal stack
|
||||
provided by sigaltstack() */
|
||||
action.sa_flags |= SA_ONSTACK;
|
||||
}
|
||||
#endif
|
||||
err = sigaction(signum, &action, &previous);
|
||||
#else
|
||||
previous = signal(signum, faulthandler_user);
|
||||
err = (previous == SIG_ERR);
|
||||
#endif
|
||||
if (err) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Py_XDECREF(user->file);
|
||||
Py_INCREF(file);
|
||||
user->file = file;
|
||||
user->fd = fd;
|
||||
user->all_threads = all_threads;
|
||||
user->previous = previous;
|
||||
user->enabled = 1;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
faulthandler_unregister(user_signal_t *user, int signum)
|
||||
{
|
||||
if (user->enabled)
|
||||
return 0;
|
||||
user->enabled = 0;
|
||||
#ifdef HAVE_SIGACTION
|
||||
(void)sigaction(signum, &user->previous, NULL);
|
||||
#else
|
||||
(void)signal(signum, user->previous);
|
||||
#endif
|
||||
Py_CLEAR(user->file);
|
||||
user->fd = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
faulthandler_unregister_py(PyObject *self, PyObject *args)
|
||||
{
|
||||
int signum;
|
||||
user_signal_t *user;
|
||||
int change;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i:unregister", &signum))
|
||||
return NULL;
|
||||
|
||||
if (signum < 1 || NSIG <= signum) {
|
||||
PyErr_SetString(PyExc_ValueError, "signal number out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user = &user_signals[signum];
|
||||
change = faulthandler_unregister(user, signum);
|
||||
return PyBool_FromLong(change);
|
||||
}
|
||||
#endif /* FAULTHANDLER_USER */
|
||||
|
||||
|
||||
static PyObject *
|
||||
faulthandler_read_null(PyObject *self, PyObject *args)
|
||||
{
|
||||
int *x = NULL, y;
|
||||
int release_gil = 0;
|
||||
if (!PyArg_ParseTuple(args, "|i:_read_null", &release_gil))
|
||||
return NULL;
|
||||
if (release_gil) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
y = *x;
|
||||
Py_END_ALLOW_THREADS
|
||||
} else
|
||||
y = *x;
|
||||
return PyLong_FromLong(y);
|
||||
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
faulthandler_sigsegv(PyObject *self, PyObject *args)
|
||||
{
|
||||
#if defined(MS_WINDOWS)
|
||||
/* faulthandler_fatal_error() restores the previous signal handler and then
|
||||
gives back the execution flow to the program. In a normal case, the
|
||||
SIGSEGV was raised by the kernel because of a fault, and so if the
|
||||
program retries to execute the same instruction, the fault will be
|
||||
raised again.
|
||||
|
||||
Here the fault is simulated by a fake SIGSEGV signal raised by the
|
||||
application. We have to raise SIGSEGV at lease twice: once for
|
||||
faulthandler_fatal_error(), and one more time for the previous signal
|
||||
handler. */
|
||||
while(1)
|
||||
raise(SIGSEGV);
|
||||
#else
|
||||
raise(SIGSEGV);
|
||||
#endif
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
faulthandler_sigfpe(PyObject *self, PyObject *args)
|
||||
{
|
||||
/* Do an integer division by zero: raise a SIGFPE on Intel CPU, but not on
|
||||
PowerPC. Use volatile to disable compile-time optimizations. */
|
||||
volatile int x = 1, y = 0, z;
|
||||
z = x / y;
|
||||
/* if the division by zero didn't raise a SIGFPE, raise it manually */
|
||||
raise(SIGFPE);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef SIGBUS
|
||||
static PyObject *
|
||||
faulthandler_sigbus(PyObject *self, PyObject *args)
|
||||
{
|
||||
raise(SIGBUS);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SIGILL
|
||||
static PyObject *
|
||||
faulthandler_sigill(PyObject *self, PyObject *args)
|
||||
{
|
||||
#if defined(MS_WINDOWS)
|
||||
/* see faulthandler_sigsegv() for the explanation about while(1) */
|
||||
while(1)
|
||||
raise(SIGILL);
|
||||
#else
|
||||
raise(SIGILL);
|
||||
#endif
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
faulthandler_fatal_error_py(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *message;
|
||||
if (!PyArg_ParseTuple(args, "y:fatal_error", &message))
|
||||
return NULL;
|
||||
Py_FatalError(message);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
|
||||
void*
|
||||
stack_overflow(void *min_sp, void *max_sp, size_t *depth)
|
||||
{
|
||||
/* allocate 4096 bytes on the stack at each call */
|
||||
unsigned char buffer[4096];
|
||||
void *sp = &buffer;
|
||||
*depth += 1;
|
||||
if (sp < min_sp || max_sp < sp)
|
||||
return sp;
|
||||
buffer[0] = 1;
|
||||
buffer[4095] = 0;
|
||||
return stack_overflow(min_sp, max_sp, depth);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
faulthandler_stack_overflow(PyObject *self)
|
||||
{
|
||||
size_t depth, size;
|
||||
void *sp = &depth, *stop;
|
||||
|
||||
depth = 0;
|
||||
stop = stack_overflow(sp - STACK_OVERFLOW_MAX_SIZE,
|
||||
sp + STACK_OVERFLOW_MAX_SIZE,
|
||||
&depth);
|
||||
if (sp < stop)
|
||||
size = stop - sp;
|
||||
else
|
||||
size = sp - stop;
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"unable to raise a stack overflow (allocated %zu bytes "
|
||||
"on the stack, %zu recursive calls)",
|
||||
size, depth);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
faulthandler_traverse(PyObject *module, visitproc visit, void *arg)
|
||||
{
|
||||
#ifdef FAULTHANDLER_USER
|
||||
unsigned int index;
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
Py_VISIT(thread.file);
|
||||
#endif
|
||||
#ifdef FAULTHANDLER_USER
|
||||
if (user_signals != NULL) {
|
||||
for (index=0; index < NSIG; index++)
|
||||
Py_VISIT(user_signals[index].file);
|
||||
}
|
||||
#endif
|
||||
Py_VISIT(fatal_error.file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(module_doc,
|
||||
"faulthandler module.");
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"enable",
|
||||
(PyCFunction)faulthandler_enable, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("enable(file=sys.stderr, all_threads=False): "
|
||||
"enable the fault handler")},
|
||||
{"disable", (PyCFunction)faulthandler_disable_py, METH_NOARGS,
|
||||
PyDoc_STR("disable(): disable the fault handler")},
|
||||
{"is_enabled", (PyCFunction)faulthandler_is_enabled, METH_NOARGS,
|
||||
PyDoc_STR("is_enabled()->bool: check if the handler is enabled")},
|
||||
{"dump_traceback",
|
||||
(PyCFunction)faulthandler_dump_traceback_py, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=False): "
|
||||
"dump the traceback of the current thread, or of all threads "
|
||||
"if all_threads is True, into file")},
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
{"dump_tracebacks_later",
|
||||
(PyCFunction)faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("dump_tracebacks_later(timeout, repeat=False, file=sys.stderr):\n"
|
||||
"dump the traceback of all threads in timeout seconds,\n"
|
||||
"or each timeout seconds if repeat is True.")},
|
||||
{"cancel_dump_tracebacks_later",
|
||||
(PyCFunction)faulthandler_cancel_dump_tracebacks_later_py, METH_NOARGS,
|
||||
PyDoc_STR("cancel_dump_tracebacks_later():\ncancel the previous call "
|
||||
"to dump_tracebacks_later().")},
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
{"register",
|
||||
(PyCFunction)faulthandler_register, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("register(signum, file=sys.stderr, all_threads=False): "
|
||||
"register an handler for the signal 'signum': dump the "
|
||||
"traceback of the current thread, or of all threads if "
|
||||
"all_threads is True, into file")},
|
||||
{"unregister",
|
||||
faulthandler_unregister_py, METH_VARARGS|METH_KEYWORDS,
|
||||
PyDoc_STR("unregister(signum): unregister the handler of the signal "
|
||||
"'signum' registered by register()")},
|
||||
#endif
|
||||
|
||||
{"_read_null", faulthandler_read_null, METH_VARARGS,
|
||||
PyDoc_STR("_read_null(release_gil=False): read from NULL, raise "
|
||||
"a SIGSEGV or SIGBUS signal depending on the platform")},
|
||||
{"_sigsegv", faulthandler_sigsegv, METH_VARARGS,
|
||||
PyDoc_STR("_sigsegv(): raise a SIGSEGV signal")},
|
||||
{"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS,
|
||||
PyDoc_STR("_sigfpe(): raise a SIGFPE signal")},
|
||||
#ifdef SIGBUS
|
||||
{"_sigbus", (PyCFunction)faulthandler_sigbus, METH_NOARGS,
|
||||
PyDoc_STR("_sigbus(): raise a SIGBUS signal")},
|
||||
#endif
|
||||
#ifdef SIGILL
|
||||
{"_sigill", (PyCFunction)faulthandler_sigill, METH_NOARGS,
|
||||
PyDoc_STR("_sigill(): raise a SIGILL signal")},
|
||||
#endif
|
||||
{"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS,
|
||||
PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")},
|
||||
#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
|
||||
{"_stack_overflow", (PyCFunction)faulthandler_stack_overflow, METH_NOARGS,
|
||||
PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")},
|
||||
#endif
|
||||
{NULL, NULL} /* terminator */
|
||||
};
|
||||
|
||||
static struct PyModuleDef module_def = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"faulthandler",
|
||||
module_doc,
|
||||
0, /* non negative size to be able to unload the module */
|
||||
module_methods,
|
||||
NULL,
|
||||
faulthandler_traverse,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_faulthandler(void)
|
||||
{
|
||||
return PyModule_Create(&module_def);
|
||||
}
|
||||
|
||||
/* Call faulthandler.enable() if PYTHONFAULTHANDLER environment variable is
|
||||
defined, or if sys._xoptions has a 'faulthandler' key. */
|
||||
|
||||
static int
|
||||
faulthandler_env_options(void)
|
||||
{
|
||||
PyObject *xoptions, *key, *module, *res;
|
||||
int enable;
|
||||
|
||||
if (!Py_GETENV("PYTHONFAULTHANDLER")) {
|
||||
xoptions = PySys_GetXOptions();
|
||||
if (xoptions == NULL)
|
||||
return -1;
|
||||
|
||||
key = PyUnicode_FromString("faulthandler");
|
||||
if (key == NULL)
|
||||
return -1;
|
||||
|
||||
enable = PyDict_Contains(xoptions, key);
|
||||
Py_DECREF(key);
|
||||
if (!enable)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
enable = 1;
|
||||
|
||||
module = PyImport_ImportModule("faulthandler");
|
||||
if (module == NULL) {
|
||||
return -1;
|
||||
}
|
||||
res = PyObject_CallMethod(module, "enable", "");
|
||||
Py_DECREF(module);
|
||||
if (res == NULL)
|
||||
return -1;
|
||||
Py_DECREF(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _PyFaulthandler_Init(void)
|
||||
{
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
int err;
|
||||
|
||||
/* Try to allocate an alternate stack for faulthandler() signal handler to
|
||||
* be able to allocate memory on the stack, even on a stack overflow. If it
|
||||
* fails, ignore the error. */
|
||||
stack.ss_flags = 0;
|
||||
stack.ss_size = SIGSTKSZ;
|
||||
stack.ss_sp = PyMem_Malloc(stack.ss_size);
|
||||
if (stack.ss_sp != NULL) {
|
||||
err = sigaltstack(&stack, NULL);
|
||||
if (err) {
|
||||
PyMem_Free(stack.ss_sp);
|
||||
stack.ss_sp = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
thread.running = 0;
|
||||
thread.file = NULL;
|
||||
thread.cancel_event = PyThread_allocate_lock();
|
||||
thread.join_event = PyThread_allocate_lock();
|
||||
if (!thread.cancel_event || !thread.join_event) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"could not allocate locks for faulthandler");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return faulthandler_env_options();
|
||||
}
|
||||
|
||||
void _PyFaulthandler_Fini(void)
|
||||
{
|
||||
#ifdef FAULTHANDLER_USER
|
||||
unsigned int i;
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_LATER
|
||||
/* later */
|
||||
faulthandler_cancel_dump_tracebacks_later();
|
||||
if (thread.cancel_event) {
|
||||
PyThread_free_lock(thread.cancel_event);
|
||||
thread.cancel_event = NULL;
|
||||
}
|
||||
if (thread.join_event) {
|
||||
PyThread_free_lock(thread.join_event);
|
||||
thread.join_event = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FAULTHANDLER_USER
|
||||
/* user */
|
||||
if (user_signals != NULL) {
|
||||
for (i=0; i < NSIG; i++)
|
||||
faulthandler_unregister(&user_signals[i], i+1);
|
||||
free(user_signals);
|
||||
user_signals = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* fatal */
|
||||
faulthandler_disable();
|
||||
#ifdef HAVE_SIGALTSTACK
|
||||
if (stack.ss_sp != NULL) {
|
||||
PyMem_Free(stack.ss_sp);
|
||||
stack.ss_sp = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
#include "Python.h"
|
||||
|
||||
/* This module is a stripped down version of _bytesio.c with a Py_UNICODE
|
||||
buffer. Most of the functionality is provided by subclassing _StringIO. */
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Py_UNICODE *buf;
|
||||
Py_ssize_t pos;
|
||||
Py_ssize_t string_size;
|
||||
size_t buf_size;
|
||||
} StringIOObject;
|
||||
|
||||
|
||||
/* Internal routine for changing the size, in terms of characters, of the
|
||||
buffer of StringIO objects. The caller should ensure that the 'size'
|
||||
argument is non-negative. Returns 0 on success, -1 otherwise. */
|
||||
static int
|
||||
resize_buffer(StringIOObject *self, size_t size)
|
||||
{
|
||||
/* Here, unsigned types are used to avoid dealing with signed integer
|
||||
overflow, which is undefined in C. */
|
||||
size_t alloc = self->buf_size;
|
||||
Py_UNICODE *new_buf = NULL;
|
||||
|
||||
assert(self->buf != NULL);
|
||||
|
||||
/* For simplicity, stay in the range of the signed type. Anyway, Python
|
||||
doesn't allow strings to be longer than this. */
|
||||
if (size > PY_SSIZE_T_MAX)
|
||||
goto overflow;
|
||||
|
||||
if (size < alloc / 2) {
|
||||
/* Major downsize; resize down to exact size. */
|
||||
alloc = size + 1;
|
||||
}
|
||||
else if (size < alloc) {
|
||||
/* Within allocated size; quick exit */
|
||||
return 0;
|
||||
}
|
||||
else if (size <= alloc * 1.125) {
|
||||
/* Moderate upsize; overallocate similar to list_resize() */
|
||||
alloc = size + (size >> 3) + (size < 9 ? 3 : 6);
|
||||
}
|
||||
else {
|
||||
/* Major upsize; resize up to exact size */
|
||||
alloc = size + 1;
|
||||
}
|
||||
|
||||
if (alloc > ((size_t)-1) / sizeof(Py_UNICODE))
|
||||
goto overflow;
|
||||
new_buf = (Py_UNICODE *)PyMem_Realloc(self->buf,
|
||||
alloc * sizeof(Py_UNICODE));
|
||||
if (new_buf == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return -1;
|
||||
}
|
||||
self->buf_size = alloc;
|
||||
self->buf = new_buf;
|
||||
|
||||
return 0;
|
||||
|
||||
overflow:
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"new buffer size too large");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Internal routine for writing a string of characters to the buffer of a
|
||||
StringIO object. Returns the number of bytes wrote, or -1 on error. */
|
||||
static Py_ssize_t
|
||||
write_str(StringIOObject *self, const Py_UNICODE *str, Py_ssize_t len)
|
||||
{
|
||||
assert(self->buf != NULL);
|
||||
assert(self->pos >= 0);
|
||||
assert(len >= 0);
|
||||
|
||||
/* This overflow check is not strictly necessary. However, it avoids us to
|
||||
deal with funky things like comparing an unsigned and a signed
|
||||
integer. */
|
||||
if (self->pos > PY_SSIZE_T_MAX - len) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"new position too large");
|
||||
return -1;
|
||||
}
|
||||
if (self->pos + len > self->string_size) {
|
||||
if (resize_buffer(self, self->pos + len) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (self->pos > self->string_size) {
|
||||
/* In case of overseek, pad with null bytes the buffer region between
|
||||
the end of stream and the current position.
|
||||
|
||||
0 lo string_size hi
|
||||
| |<---used--->|<----------available----------->|
|
||||
| | <--to pad-->|<---to write---> |
|
||||
0 buf positon
|
||||
|
||||
*/
|
||||
memset(self->buf + self->string_size, '\0',
|
||||
(self->pos - self->string_size) * sizeof(Py_UNICODE));
|
||||
}
|
||||
|
||||
/* Copy the data to the internal buffer, overwriting some of the
|
||||
existing data if self->pos < self->string_size. */
|
||||
memcpy(self->buf + self->pos, str, len * sizeof(Py_UNICODE));
|
||||
self->pos += len;
|
||||
|
||||
/* Set the new length of the internal string if it has changed */
|
||||
if (self->string_size < self->pos) {
|
||||
self->string_size = self->pos;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_getvalue(StringIOObject *self)
|
||||
{
|
||||
return PyUnicode_FromUnicode(self->buf, self->string_size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_tell(StringIOObject *self)
|
||||
{
|
||||
return PyLong_FromSsize_t(self->pos);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_read(StringIOObject *self, PyObject *args)
|
||||
{
|
||||
Py_ssize_t size, n;
|
||||
Py_UNICODE *output;
|
||||
PyObject *arg = Py_None;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:read", &arg))
|
||||
return NULL;
|
||||
|
||||
if (PyLong_Check(arg)) {
|
||||
size = PyLong_AsSsize_t(arg);
|
||||
if (size == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
}
|
||||
else if (arg == Py_None) {
|
||||
/* Read until EOF is reached, by default. */
|
||||
size = -1;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'",
|
||||
Py_TYPE(arg)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* adjust invalid sizes */
|
||||
n = self->string_size - self->pos;
|
||||
if (size < 0 || size > n) {
|
||||
size = n;
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
}
|
||||
|
||||
assert(self->buf != NULL);
|
||||
output = self->buf + self->pos;
|
||||
self->pos += size;
|
||||
|
||||
return PyUnicode_FromUnicode(output, size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_truncate(StringIOObject *self, PyObject *args)
|
||||
{
|
||||
Py_ssize_t size;
|
||||
PyObject *arg = Py_None;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:truncate", &arg))
|
||||
return NULL;
|
||||
|
||||
if (PyLong_Check(arg)) {
|
||||
size = PyLong_AsSsize_t(arg);
|
||||
if (size == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
}
|
||||
else if (arg == Py_None) {
|
||||
/* Truncate to current position if no argument is passed. */
|
||||
size = self->pos;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'",
|
||||
Py_TYPE(arg)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Negative size value %zd", size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size < self->string_size) {
|
||||
self->string_size = size;
|
||||
if (resize_buffer(self, size) < 0)
|
||||
return NULL;
|
||||
}
|
||||
self->pos = size;
|
||||
|
||||
return PyLong_FromSsize_t(size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_seek(StringIOObject *self, PyObject *args)
|
||||
{
|
||||
Py_ssize_t pos;
|
||||
int mode = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &mode))
|
||||
return NULL;
|
||||
|
||||
if (mode != 0 && mode != 1 && mode != 2) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Invalid whence (%i, should be 0, 1 or 2)", mode);
|
||||
return NULL;
|
||||
}
|
||||
else if (pos < 0 && mode == 0) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Negative seek position %zd", pos);
|
||||
return NULL;
|
||||
}
|
||||
else if (mode != 0 && pos != 0) {
|
||||
PyErr_SetString(PyExc_IOError,
|
||||
"Can't do nonzero cur-relative seeks");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mode 0: offset relative to beginning of the string.
|
||||
mode 1: no change to current position.
|
||||
mode 2: change position to end of file. */
|
||||
if (mode == 1) {
|
||||
pos = self->pos;
|
||||
}
|
||||
else if (mode == 2) {
|
||||
pos = self->string_size;
|
||||
}
|
||||
|
||||
self->pos = pos;
|
||||
|
||||
return PyLong_FromSsize_t(self->pos);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_write(StringIOObject *self, PyObject *obj)
|
||||
{
|
||||
const Py_UNICODE *str;
|
||||
Py_ssize_t size;
|
||||
Py_ssize_t n = 0;
|
||||
|
||||
if (PyUnicode_Check(obj)) {
|
||||
str = PyUnicode_AsUnicode(obj);
|
||||
size = PyUnicode_GetSize(obj);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError, "string argument expected, got '%s'",
|
||||
Py_TYPE(obj)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
n = write_str(self, str, size);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyLong_FromSsize_t(n);
|
||||
}
|
||||
|
||||
static void
|
||||
stringio_dealloc(StringIOObject *self)
|
||||
{
|
||||
PyMem_Free(self->buf);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
StringIOObject *self;
|
||||
|
||||
assert(type != NULL && type->tp_alloc != NULL);
|
||||
self = (StringIOObject *)type->tp_alloc(type, 0);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
self->string_size = 0;
|
||||
self->pos = 0;
|
||||
self->buf_size = 0;
|
||||
self->buf = (Py_UNICODE *)PyMem_Malloc(0);
|
||||
if (self->buf == NULL) {
|
||||
Py_DECREF(self);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
static struct PyMethodDef stringio_methods[] = {
|
||||
{"getvalue", (PyCFunction)stringio_getvalue, METH_VARARGS, NULL},
|
||||
{"read", (PyCFunction)stringio_read, METH_VARARGS, NULL},
|
||||
{"tell", (PyCFunction)stringio_tell, METH_NOARGS, NULL},
|
||||
{"truncate", (PyCFunction)stringio_truncate, METH_VARARGS, NULL},
|
||||
{"seek", (PyCFunction)stringio_seek, METH_VARARGS, NULL},
|
||||
{"write", (PyCFunction)stringio_write, METH_O, NULL},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject StringIO_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_stringio._StringIO", /*tp_name*/
|
||||
sizeof(StringIOObject), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
(destructor)stringio_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash*/
|
||||
0, /*tp_call*/
|
||||
0, /*tp_str*/
|
||||
0, /*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
||||
0, /*tp_doc*/
|
||||
0, /*tp_traverse*/
|
||||
0, /*tp_clear*/
|
||||
0, /*tp_richcompare*/
|
||||
0, /*tp_weaklistoffset*/
|
||||
0, /*tp_iter*/
|
||||
0, /*tp_iternext*/
|
||||
stringio_methods, /*tp_methods*/
|
||||
0, /*tp_members*/
|
||||
0, /*tp_getset*/
|
||||
0, /*tp_base*/
|
||||
0, /*tp_dict*/
|
||||
0, /*tp_descr_get*/
|
||||
0, /*tp_descr_set*/
|
||||
0, /*tp_dictoffset*/
|
||||
0, /*tp_init*/
|
||||
0, /*tp_alloc*/
|
||||
stringio_new, /*tp_new*/
|
||||
};
|
||||
|
||||
static struct PyModuleDef _stringiomodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_stringio",
|
||||
NULL,
|
||||
-1,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__stringio(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
if (PyType_Ready(&StringIO_Type) < 0)
|
||||
return NULL;
|
||||
m = PyModule_Create(&_stringiomodule);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
Py_INCREF(&StringIO_Type);
|
||||
if (PyModule_AddObject(m, "_StringIO", (PyObject *)&StringIO_Type) < 0)
|
||||
return NULL;
|
||||
return m;
|
||||
}
|
||||
+1707
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1259
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 4 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2002 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.02 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available at through the world-wide-web at |
|
||||
| http://www.php.net/license/2_02.txt. |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
#include "php.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include "ext/standard/pageinfo.h"
|
||||
#include "safe_mode.h"
|
||||
#include "SAPI.h"
|
||||
#include "php_globals.h"
|
||||
|
||||
|
||||
/*
|
||||
* php_checkuid
|
||||
*
|
||||
* This function has four modes:
|
||||
*
|
||||
* 0 - return invalid (0) if file does not exist
|
||||
* 1 - return valid (1) if file does not exist
|
||||
* 2 - if file does not exist, check directory
|
||||
* 3 - only check directory (needed for mkdir)
|
||||
*/
|
||||
|
||||
PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode)
|
||||
{
|
||||
struct stat sb;
|
||||
int ret;
|
||||
long uid=0L, gid=0L, duid=0L, dgid=0L;
|
||||
char path[MAXPATHLEN];
|
||||
char *s;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if (!filename) {
|
||||
return 0; /* path must be provided */
|
||||
}
|
||||
|
||||
if (fopen_mode) {
|
||||
if (fopen_mode[0] == 'r') {
|
||||
mode = CHECKUID_DISALLOW_FILE_NOT_EXISTS;
|
||||
} else {
|
||||
mode = CHECKUID_CHECK_FILE_AND_DIR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If given filepath is a URL, allow - safe mode stuff
|
||||
* related to URL's is checked in individual functions
|
||||
*/
|
||||
if (!strncasecmp(filename,"http://", 7) || !strncasecmp(filename,"ftp://", 6)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* First we see if the file is owned by the same user...
|
||||
* If that fails, passthrough and check directory...
|
||||
*/
|
||||
if (mode != CHECKUID_ALLOW_ONLY_DIR) {
|
||||
VCWD_REALPATH(filename, path);
|
||||
ret = VCWD_STAT(path, &sb);
|
||||
if (ret < 0) {
|
||||
if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) {
|
||||
php_error(E_WARNING, "Unable to access %s", filename);
|
||||
return 0;
|
||||
} else if (mode == CHECKUID_ALLOW_FILE_NOT_EXISTS)
|
||||
php_error(E_WARNING, "Unable to access %s", filename);{
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
uid = sb.st_uid;
|
||||
gid = sb.st_gid;
|
||||
if (uid == php_getuid()) {
|
||||
return 1;
|
||||
} else if (PG(safe_mode_gid) && gid == php_getgid()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Trim off filename */
|
||||
if ((s = strrchr(path, DEFAULT_SLASH))) {
|
||||
if (s == path)
|
||||
path[1] = '\0';
|
||||
else
|
||||
*s = '\0';
|
||||
}
|
||||
} else { /* CHECKUID_ALLOW_ONLY_DIR */
|
||||
s = strrchr(filename, DEFAULT_SLASH);
|
||||
|
||||
if (s == filename) {
|
||||
/* root dir */
|
||||
path[0] = DEFAULT_SLASH;
|
||||
path[1] = '\0';
|
||||
} else if (s) {
|
||||
*s = '\0';
|
||||
VCWD_REALPATH(filename, path);
|
||||
*s = DEFAULT_SLASH;
|
||||
} else {
|
||||
VCWD_GETCWD(path, MAXPATHLEN);
|
||||
}
|
||||
} /* end CHECKUID_ALLOW_ONLY_DIR */
|
||||
|
||||
if (mode != CHECKUID_ALLOW_ONLY_FILE) {
|
||||
/* check directory */
|
||||
ret = VCWD_STAT(path, &sb);
|
||||
if (ret < 0) {
|
||||
php_error(E_WARNING, "Unable to access %s", filename);
|
||||
return 0;
|
||||
}
|
||||
duid = sb.st_uid;
|
||||
dgid = sb.st_gid;
|
||||
if (duid == php_getuid()) {
|
||||
return 1;
|
||||
} else if (PG(safe_mode_gid) && dgid == php_getgid()) {
|
||||
return 1;
|
||||
} else {
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if (SG(rfc1867_uploaded_files)) {
|
||||
if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == CHECKUID_ALLOW_ONLY_DIR) {
|
||||
uid = duid;
|
||||
gid = dgid;
|
||||
if (s) {
|
||||
*s = 0;
|
||||
}
|
||||
}
|
||||
if (PG(safe_mode_gid)) {
|
||||
php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", php_getuid(), php_getgid(), filename, uid, gid);
|
||||
} else {
|
||||
php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid is %ld is not allowed to access %s owned by uid %ld", php_getuid(), filename, uid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PHPAPI char *php_get_current_user()
|
||||
{
|
||||
struct passwd *pwd;
|
||||
struct stat *pstat;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if (SG(request_info).current_user) {
|
||||
return SG(request_info).current_user;
|
||||
}
|
||||
|
||||
/* FIXME: I need to have this somehow handled if
|
||||
USE_SAPI is defined, because cgi will also be
|
||||
interfaced in USE_SAPI */
|
||||
|
||||
pstat = sapi_get_stat(TSRMLS_C);
|
||||
|
||||
if (!pstat) {
|
||||
return empty_string;
|
||||
}
|
||||
|
||||
if ((pwd=getpwuid(pstat->st_uid))==NULL) {
|
||||
return empty_string;
|
||||
}
|
||||
SG(request_info).current_user_length = strlen(pwd->pw_name);
|
||||
SG(request_info).current_user = estrndup(pwd->pw_name, SG(request_info).current_user_length);
|
||||
|
||||
return SG(request_info).current_user;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: sw=4 ts=4 fdm=marker
|
||||
* vim<600: sw=4 ts=4
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 4 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2002 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.02 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available at through the world-wide-web at |
|
||||
| http://www.php.net/license/2_02.txt. |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
#include "php.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include "ext/standard/pageinfo.h"
|
||||
#include "safe_mode.h"
|
||||
#include "SAPI.h"
|
||||
#include "php_globals.h"
|
||||
|
||||
|
||||
/*
|
||||
* php_checkuid
|
||||
*
|
||||
* This function has four modes:
|
||||
*
|
||||
* 0 - return invalid (0) if file does not exist
|
||||
* 1 - return valid (1) if file does not exist
|
||||
* 2 - if file does not exist, check directory
|
||||
* 3 - only check directory (needed for mkdir)
|
||||
*/
|
||||
|
||||
PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode)
|
||||
{
|
||||
struct stat sb;
|
||||
int ret;
|
||||
long uid=0L, gid=0L, duid=0L, dgid=0L;
|
||||
char path[MAXPATHLEN];
|
||||
char *s;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if (!filename) {
|
||||
return 0; /* path must be provided */
|
||||
}
|
||||
|
||||
if (fopen_mode) {
|
||||
if (fopen_mode[0] == 'r') {
|
||||
mode = CHECKUID_DISALLOW_FILE_NOT_EXISTS;
|
||||
} else {
|
||||
mode = CHECKUID_CHECK_FILE_AND_DIR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If given filepath is a URL, allow - safe mode stuff
|
||||
* related to URL's is checked in individual functions
|
||||
*/
|
||||
if (!strncasecmp(filename,"http://", 7) || !strncasecmp(filename,"ftp://", 6)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* First we see if the file is owned by the same user...
|
||||
* If that fails, passthrough and check directory...
|
||||
*/
|
||||
if (mode != CHECKUID_ALLOW_ONLY_DIR) {
|
||||
VCWD_REALPATH(filename, path);
|
||||
ret = VCWD_STAT(path, &sb);
|
||||
if (ret < 0) {
|
||||
if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) {
|
||||
php_error(E_WARNING, "Unable to access %s", filename);
|
||||
return 0;
|
||||
} else if (mode == CHECKUID_ALLOW_FILE_NOT_EXISTS) {
|
||||
php_error(E_WARNING, "Unable to access %s", filename);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
uid = sb.st_uid;
|
||||
gid = sb.st_gid;
|
||||
if (uid == php_getuid()) {
|
||||
return 1;
|
||||
} else if (PG(safe_mode_gid) && gid == php_getgid()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Trim off filename */
|
||||
if ((s = strrchr(path, DEFAULT_SLASH))) {
|
||||
if (s == path)
|
||||
path[1] = '\0';
|
||||
else
|
||||
*s = '\0';
|
||||
}
|
||||
} else { /* CHECKUID_ALLOW_ONLY_DIR */
|
||||
s = strrchr(filename, DEFAULT_SLASH);
|
||||
|
||||
if (s == filename) {
|
||||
/* root dir */
|
||||
path[0] = DEFAULT_SLASH;
|
||||
path[1] = '\0';
|
||||
} else if (s) {
|
||||
*s = '\0';
|
||||
VCWD_REALPATH(filename, path);
|
||||
*s = DEFAULT_SLASH;
|
||||
} else {
|
||||
VCWD_GETCWD(path, MAXPATHLEN);
|
||||
}
|
||||
} /* end CHECKUID_ALLOW_ONLY_DIR */
|
||||
|
||||
if (mode != CHECKUID_ALLOW_ONLY_FILE) {
|
||||
/* check directory */
|
||||
ret = VCWD_STAT(path, &sb);
|
||||
if (ret < 0) {
|
||||
php_error(E_WARNING, "Unable to access %s", filename);
|
||||
return 0;
|
||||
}
|
||||
duid = sb.st_uid;
|
||||
dgid = sb.st_gid;
|
||||
if (duid == php_getuid()) {
|
||||
return 1;
|
||||
} else if (PG(safe_mode_gid) && dgid == php_getgid()) {
|
||||
return 1;
|
||||
} else {
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if (SG(rfc1867_uploaded_files)) {
|
||||
if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == CHECKUID_ALLOW_ONLY_DIR) {
|
||||
uid = duid;
|
||||
gid = dgid;
|
||||
if (s) {
|
||||
*s = 0;
|
||||
}
|
||||
}
|
||||
if (PG(safe_mode_gid)) {
|
||||
php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", php_getuid(), php_getgid(), filename, uid, gid);
|
||||
} else {
|
||||
php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid is %ld is not allowed to access %s owned by uid %ld", php_getuid(), filename, uid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PHPAPI char *php_get_current_user()
|
||||
{
|
||||
struct passwd *pwd;
|
||||
struct stat *pstat;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if (SG(request_info).current_user) {
|
||||
return SG(request_info).current_user;
|
||||
}
|
||||
|
||||
/* FIXME: I need to have this somehow handled if
|
||||
USE_SAPI is defined, because cgi will also be
|
||||
interfaced in USE_SAPI */
|
||||
|
||||
pstat = sapi_get_stat(TSRMLS_C);
|
||||
|
||||
if (!pstat) {
|
||||
return empty_string;
|
||||
}
|
||||
|
||||
if ((pwd=getpwuid(pstat->st_uid))==NULL) {
|
||||
return empty_string;
|
||||
}
|
||||
SG(request_info).current_user_length = strlen(pwd->pw_name);
|
||||
SG(request_info).current_user = estrndup(pwd->pw_name, SG(request_info).current_user_length);
|
||||
|
||||
return SG(request_info).current_user;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: sw=4 ts=4 fdm=marker
|
||||
* vim<600: sw=4 ts=4
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
+12477
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+12477
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user