Merge pull request #284 from gwsystems/replay-sigalrm

feat: replay deferred sigalrms
master
Sean McBride 3 years ago committed by GitHub
commit 79ca1f04ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -99,7 +99,9 @@
"sandbox_state_history.h": "c",
"sandbox_set_as_running_user.h": "c",
"scheduler.h": "c",
"sandbox_set_as_returned.h": "c"
"sandbox_set_as_returned.h": "c",
"software_interrupt_counts.h": "c",
"sandbox_set_as_running_sys.h": "c"
},
"files.exclude": {
"**/.git": true,

@ -1 +1,3 @@
This is a state transition diagram of a sandbox. This maps to the state transition functions defined in `runtime/include/sandbox_set_as_*.h`
Technically, this does not capture all state transitions to or from SANDBOX_INTERRUPTED, as any state can be interrupted by a SIGALRM and this would clutter the diagram. The only transitions shown to or from SANDBOX_INTERRUPTED are those leading to SANDBOX_PREEMPTED, as this reflects actual changes to a sandbox within the scheduler. All other transitions to/from SANDBOX_INTERRUPTED are mostly concerned with preventing scheduler execution time from being counted against sandbox execution times.

@ -2,11 +2,19 @@ digraph {
Uninitialized -> Initialized
Initialized -> {Runnable Error}
Runnable -> Running_Sys
Running_User -> Running_Sys [label="interrupt"]
Running_User -> Interrupted [label="interrupt"]
Running_User -> Running_Sys [label="syscall"]
Interrupted -> Preempted [label="preempt"]
Interrupted -> Running_User
Running_Sys -> Asleep [label="sleep"]
Running_Sys -> {Error Returned}
Running_Sys -> Running_User [label="return"]
Running_Sys -> Preempted [label="preempt"]
Preempted -> Running_User
Returned -> Complete [label="exit_success"]
Asleep -> Runnable [label="wakeup"]

@ -4,154 +4,173 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: %3 Pages: 1 -->
<svg width="650pt" height="437pt"
viewBox="0.00 0.00 649.79 437.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 433)">
<svg width="480pt" height="524pt"
viewBox="0.00 0.00 479.99 524.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 520)">
<title>%3</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-433 645.79,-433 645.79,4 -4,4"/>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-520 475.99,-520 475.99,4 -4,4"/>
<!-- Uninitialized -->
<g id="node1" class="node">
<title>Uninitialized</title>
<ellipse fill="none" stroke="black" cx="70.19" cy="-411" rx="70.39" ry="18"/>
<text text-anchor="middle" x="70.19" y="-407.3" font-family="Times,serif" font-size="14.00">Uninitialized</text>
<ellipse fill="none" stroke="black" cx="344.6" cy="-498" rx="70.39" ry="18"/>
<text text-anchor="middle" x="344.6" y="-494.3" font-family="Times,serif" font-size="14.00">Uninitialized</text>
</g>
<!-- Initialized -->
<g id="node2" class="node">
<title>Initialized</title>
<ellipse fill="none" stroke="black" cx="70.19" cy="-338" rx="57.39" ry="18"/>
<text text-anchor="middle" x="70.19" y="-334.3" font-family="Times,serif" font-size="14.00">Initialized</text>
<ellipse fill="none" stroke="black" cx="344.6" cy="-425" rx="57.39" ry="18"/>
<text text-anchor="middle" x="344.6" y="-421.3" font-family="Times,serif" font-size="14.00">Initialized</text>
</g>
<!-- Uninitialized&#45;&gt;Initialized -->
<g id="edge1" class="edge">
<title>Uninitialized&#45;&gt;Initialized</title>
<path fill="none" stroke="black" d="M70.19,-392.81C70.19,-384.79 70.19,-375.05 70.19,-366.07"/>
<polygon fill="black" stroke="black" points="73.69,-366.03 70.19,-356.03 66.69,-366.03 73.69,-366.03"/>
<path fill="none" stroke="black" d="M344.6,-479.81C344.6,-471.79 344.6,-462.05 344.6,-453.07"/>
<polygon fill="black" stroke="black" points="348.1,-453.03 344.6,-443.03 341.1,-453.03 348.1,-453.03"/>
</g>
<!-- Runnable -->
<g id="node3" class="node">
<title>Runnable</title>
<ellipse fill="none" stroke="black" cx="266.19" cy="-265" rx="54.69" ry="18"/>
<text text-anchor="middle" x="266.19" y="-261.3" font-family="Times,serif" font-size="14.00">Runnable</text>
<ellipse fill="none" stroke="black" cx="232.6" cy="-352" rx="54.69" ry="18"/>
<text text-anchor="middle" x="232.6" y="-348.3" font-family="Times,serif" font-size="14.00">Runnable</text>
</g>
<!-- Initialized&#45;&gt;Runnable -->
<g id="edge2" class="edge">
<title>Initialized&#45;&gt;Runnable</title>
<path fill="none" stroke="black" d="M106.14,-323.98C138.57,-312.23 186.39,-294.91 221.32,-282.26"/>
<polygon fill="black" stroke="black" points="222.59,-285.52 230.8,-278.82 220.21,-278.94 222.59,-285.52"/>
<path fill="none" stroke="black" d="M320.29,-408.59C304.3,-398.45 283.15,-385.05 265.63,-373.94"/>
<polygon fill="black" stroke="black" points="267.17,-370.77 256.85,-368.38 263.42,-376.69 267.17,-370.77"/>
</g>
<!-- Error -->
<g id="node4" class="node">
<title>Error</title>
<ellipse fill="none" stroke="black" cx="52.19" cy="-105" rx="36.29" ry="18"/>
<text text-anchor="middle" x="52.19" y="-101.3" font-family="Times,serif" font-size="14.00">Error</text>
<ellipse fill="none" stroke="black" cx="435.6" cy="-192" rx="36.29" ry="18"/>
<text text-anchor="middle" x="435.6" y="-188.3" font-family="Times,serif" font-size="14.00">Error</text>
</g>
<!-- Initialized&#45;&gt;Error -->
<g id="edge3" class="edge">
<title>Initialized&#45;&gt;Error</title>
<path fill="none" stroke="black" d="M68.84,-319.64C65.73,-279.74 58.06,-181.27 54.32,-133.24"/>
<polygon fill="black" stroke="black" points="57.8,-132.95 53.54,-123.25 50.83,-133.49 57.8,-132.95"/>
<path fill="none" stroke="black" d="M351.29,-407.01C367,-367.13 406.34,-267.27 425.2,-219.4"/>
<polygon fill="black" stroke="black" points="428.49,-220.6 428.9,-210.01 421.97,-218.03 428.49,-220.6"/>
</g>
<!-- Running_Sys -->
<g id="node5" class="node">
<title>Running_Sys</title>
<ellipse fill="none" stroke="black" cx="266.19" cy="-192" rx="70.69" ry="18"/>
<text text-anchor="middle" x="266.19" y="-188.3" font-family="Times,serif" font-size="14.00">Running_Sys</text>
<ellipse fill="none" stroke="black" cx="232.6" cy="-279" rx="70.69" ry="18"/>
<text text-anchor="middle" x="232.6" y="-275.3" font-family="Times,serif" font-size="14.00">Running_Sys</text>
</g>
<!-- Runnable&#45;&gt;Running_Sys -->
<g id="edge4" class="edge">
<title>Runnable&#45;&gt;Running_Sys</title>
<path fill="none" stroke="black" d="M266.19,-246.81C266.19,-238.79 266.19,-229.05 266.19,-220.07"/>
<polygon fill="black" stroke="black" points="269.69,-220.03 266.19,-210.03 262.69,-220.03 269.69,-220.03"/>
<path fill="none" stroke="black" d="M232.6,-333.81C232.6,-325.79 232.6,-316.05 232.6,-307.07"/>
<polygon fill="black" stroke="black" points="236.1,-307.03 232.6,-297.03 229.1,-307.03 236.1,-307.03"/>
</g>
<!-- Running_Sys&#45;&gt;Error -->
<g id="edge7" class="edge">
<g id="edge10" class="edge">
<title>Running_Sys&#45;&gt;Error</title>
<path fill="none" stroke="black" d="M202.68,-183.96C173.38,-178.94 138.83,-170.41 110.19,-156 96.75,-149.24 83.86,-138.69 73.66,-129.01"/>
<polygon fill="black" stroke="black" points="76,-126.4 66.42,-121.87 71.08,-131.38 76,-126.4"/>
<path fill="none" stroke="black" d="M278.15,-265.13C297.66,-259.14 320.53,-251.46 340.6,-243 362.39,-233.81 385.92,-221.45 404.1,-211.32"/>
<polygon fill="black" stroke="black" points="406.1,-214.21 413.09,-206.25 402.66,-208.11 406.1,-214.21"/>
</g>
<!-- Running_User -->
<g id="node6" class="node">
<title>Running_User</title>
<ellipse fill="none" stroke="black" cx="226.19" cy="-18" rx="76.89" ry="18"/>
<text text-anchor="middle" x="226.19" y="-14.3" font-family="Times,serif" font-size="14.00">Running_User</text>
<ellipse fill="none" stroke="black" cx="304.6" cy="-192" rx="76.89" ry="18"/>
<text text-anchor="middle" x="304.6" y="-188.3" font-family="Times,serif" font-size="14.00">Running_User</text>
</g>
<!-- Running_Sys&#45;&gt;Running_User -->
<g id="edge9" class="edge">
<g id="edge12" class="edge">
<title>Running_Sys&#45;&gt;Running_User</title>
<path fill="none" stroke="black" d="M256.46,-174C249.29,-160.59 239.98,-141.19 235.19,-123 228.52,-97.63 226.52,-67.69 226.04,-46.47"/>
<polygon fill="black" stroke="black" points="229.53,-46.19 225.91,-36.24 222.53,-46.28 229.53,-46.19"/>
<text text-anchor="middle" x="258.19" y="-101.3" font-family="Times,serif" font-size="14.00">return</text>
<path fill="none" stroke="black" d="M260.51,-262.41C268.39,-257.03 276.48,-250.45 282.6,-243 288.24,-236.12 292.71,-227.57 296.09,-219.49"/>
<polygon fill="black" stroke="black" points="299.41,-220.61 299.7,-210.02 292.87,-218.12 299.41,-220.61"/>
<text text-anchor="middle" x="313.6" y="-231.8" font-family="Times,serif" font-size="14.00">return</text>
</g>
<!-- Asleep -->
<g id="node7" class="node">
<g id="node9" class="node">
<title>Asleep</title>
<ellipse fill="none" stroke="black" cx="600.19" cy="-105" rx="41.69" ry="18"/>
<text text-anchor="middle" x="600.19" y="-101.3" font-family="Times,serif" font-size="14.00">Asleep</text>
<ellipse fill="none" stroke="black" cx="41.6" cy="-192" rx="41.69" ry="18"/>
<text text-anchor="middle" x="41.6" y="-188.3" font-family="Times,serif" font-size="14.00">Asleep</text>
</g>
<!-- Running_Sys&#45;&gt;Asleep -->
<g id="edge6" class="edge">
<g id="edge9" class="edge">
<title>Running_Sys&#45;&gt;Asleep</title>
<path fill="none" stroke="black" d="M318.7,-179.8C375.79,-167.3 469.52,-145.8 549.19,-123 552.38,-122.09 555.66,-121.1 558.95,-120.08"/>
<polygon fill="black" stroke="black" points="560.31,-123.32 568.77,-116.95 558.18,-116.65 560.31,-123.32"/>
<text text-anchor="middle" x="499.19" y="-144.8" font-family="Times,serif" font-size="14.00">sleep</text>
<path fill="none" stroke="black" d="M195.57,-263.48C180.32,-257.35 162.52,-250.03 146.6,-243 123.31,-232.73 97.5,-220.45 77.3,-210.63"/>
<polygon fill="black" stroke="black" points="78.68,-207.41 68.16,-206.17 75.61,-213.7 78.68,-207.41"/>
<text text-anchor="middle" x="165.6" y="-231.8" font-family="Times,serif" font-size="14.00">sleep</text>
</g>
<!-- Returned -->
<g id="node8" class="node">
<g id="node10" class="node">
<title>Returned</title>
<ellipse fill="none" stroke="black" cx="486.19" cy="-105" rx="53.89" ry="18"/>
<text text-anchor="middle" x="486.19" y="-101.3" font-family="Times,serif" font-size="14.00">Returned</text>
<ellipse fill="none" stroke="black" cx="155.6" cy="-192" rx="53.89" ry="18"/>
<text text-anchor="middle" x="155.6" y="-188.3" font-family="Times,serif" font-size="14.00">Returned</text>
</g>
<!-- Running_Sys&#45;&gt;Returned -->
<g id="edge8" class="edge">
<g id="edge11" class="edge">
<title>Running_Sys&#45;&gt;Returned</title>
<path fill="none" stroke="black" d="M312.74,-178.34C333.61,-172.25 358.38,-164.45 380.19,-156 403.62,-146.93 429.1,-134.98 449.21,-125.04"/>
<polygon fill="black" stroke="black" points="450.95,-128.09 458.33,-120.49 447.82,-121.82 450.95,-128.09"/>
<path fill="none" stroke="black" d="M217.38,-261.21C205.92,-248.55 190.04,-231.03 177.25,-216.91"/>
<polygon fill="black" stroke="black" points="179.82,-214.53 170.52,-209.47 174.64,-219.23 179.82,-214.53"/>
</g>
<!-- Running_User&#45;&gt;Running_Sys -->
<g id="edge6" class="edge">
<title>Running_User&#45;&gt;Running_Sys</title>
<path fill="none" stroke="black" d="M258.8,-206.54C248.87,-211.65 239.57,-218.61 233.6,-228 229.32,-234.72 227.98,-243.05 228.02,-250.97"/>
<polygon fill="black" stroke="black" points="224.54,-251.26 228.74,-260.98 231.52,-250.75 224.54,-251.26"/>
<text text-anchor="middle" x="258.1" y="-231.8" font-family="Times,serif" font-size="14.00">syscall</text>
</g>
<!-- Interrupted -->
<g id="node7" class="node">
<title>Interrupted</title>
<ellipse fill="none" stroke="black" cx="348.6" cy="-105" rx="64.99" ry="18"/>
<text text-anchor="middle" x="348.6" y="-101.3" font-family="Times,serif" font-size="14.00">Interrupted</text>
</g>
<!-- Running_User&#45;&gt;Interrupted -->
<g id="edge5" class="edge">
<title>Running_User&#45;&gt;Interrupted</title>
<path fill="none" stroke="black" d="M313.5,-173.8C319.74,-161.74 328.2,-145.4 335.25,-131.79"/>
<polygon fill="black" stroke="black" points="338.37,-133.38 339.86,-122.89 332.15,-130.16 338.37,-133.38"/>
<text text-anchor="middle" x="361.6" y="-144.8" font-family="Times,serif" font-size="14.00">interrupt</text>
</g>
<!-- Interrupted&#45;&gt;Running_User -->
<g id="edge8" class="edge">
<title>Interrupted&#45;&gt;Running_User</title>
<path fill="none" stroke="black" d="M333.3,-122.56C328.75,-128.12 324.05,-134.55 320.6,-141 316.76,-148.15 313.66,-156.37 311.25,-164.05"/>
<polygon fill="black" stroke="black" points="307.87,-163.13 308.46,-173.71 314.59,-165.07 307.87,-163.13"/>
</g>
<!-- Preempted -->
<g id="node9" class="node">
<g id="node8" class="node">
<title>Preempted</title>
<ellipse fill="none" stroke="black" cx="352.19" cy="-105" rx="61.99" ry="18"/>
<text text-anchor="middle" x="352.19" y="-101.3" font-family="Times,serif" font-size="14.00">Preempted</text>
<ellipse fill="none" stroke="black" cx="271.6" cy="-18" rx="61.99" ry="18"/>
<text text-anchor="middle" x="271.6" y="-14.3" font-family="Times,serif" font-size="14.00">Preempted</text>
</g>
<!-- Running_Sys&#45;&gt;Preempted -->
<g id="edge10" class="edge">
<title>Running_Sys&#45;&gt;Preempted</title>
<path fill="none" stroke="black" d="M283.18,-174.21C296.11,-161.43 314.05,-143.7 328.4,-129.52"/>
<polygon fill="black" stroke="black" points="330.88,-131.99 335.53,-122.47 325.96,-127.01 330.88,-131.99"/>
<text text-anchor="middle" x="345.69" y="-144.8" font-family="Times,serif" font-size="14.00">preempt</text>
<!-- Interrupted&#45;&gt;Preempted -->
<g id="edge7" class="edge">
<title>Interrupted&#45;&gt;Preempted</title>
<path fill="none" stroke="black" d="M333.38,-87.21C321.92,-74.55 306.04,-57.03 293.25,-42.91"/>
<polygon fill="black" stroke="black" points="295.82,-40.53 286.52,-35.47 290.64,-45.23 295.82,-40.53"/>
<text text-anchor="middle" x="345.1" y="-57.8" font-family="Times,serif" font-size="14.00">preempt</text>
</g>
<!-- Running_User&#45;&gt;Running_Sys -->
<g id="edge5" class="edge">
<title>Running_User&#45;&gt;Running_Sys</title>
<path fill="none" stroke="black" d="M196.06,-34.66C163.08,-53.98 117.48,-88.54 138.19,-123 153.49,-148.45 181.89,-164.89 208.05,-175.21"/>
<polygon fill="black" stroke="black" points="206.97,-178.54 217.56,-178.74 209.4,-171.98 206.97,-178.54"/>
<text text-anchor="middle" x="171.19" y="-101.3" font-family="Times,serif" font-size="14.00">interrupt</text>
<!-- Preempted&#45;&gt;Running_User -->
<g id="edge13" class="edge">
<title>Preempted&#45;&gt;Running_User</title>
<path fill="none" stroke="black" d="M270.09,-36.18C268.73,-57.01 267.83,-93.05 274.6,-123 277.85,-137.4 284.19,-152.51 290.2,-164.82"/>
<polygon fill="black" stroke="black" points="287.23,-166.71 294.89,-174.03 293.47,-163.53 287.23,-166.71"/>
</g>
<!-- Asleep&#45;&gt;Runnable -->
<g id="edge13" class="edge">
<g id="edge15" class="edge">
<title>Asleep&#45;&gt;Runnable</title>
<path fill="none" stroke="black" d="M579.26,-120.81C563.89,-131.34 542.26,-145.43 522.19,-156 450.82,-193.58 363.46,-228.15 311.45,-247.6"/>
<polygon fill="black" stroke="black" points="310,-244.41 301.85,-251.17 312.44,-250.97 310,-244.41"/>
<text text-anchor="middle" x="512.69" y="-188.3" font-family="Times,serif" font-size="14.00">wakeup</text>
<path fill="none" stroke="black" d="M46.7,-210.08C54.3,-232.82 70.67,-272.97 97.6,-297 120.32,-317.28 151.62,-330.45 178.36,-338.69"/>
<polygon fill="black" stroke="black" points="177.4,-342.06 187.98,-341.51 179.37,-335.34 177.4,-342.06"/>
<text text-anchor="middle" x="125.1" y="-275.3" font-family="Times,serif" font-size="14.00">wakeup</text>
</g>
<!-- Complete -->
<g id="node10" class="node">
<g id="node11" class="node">
<title>Complete</title>
<ellipse fill="none" stroke="black" cx="486.19" cy="-18" rx="55.49" ry="18"/>
<text text-anchor="middle" x="486.19" y="-14.3" font-family="Times,serif" font-size="14.00">Complete</text>
<ellipse fill="none" stroke="black" cx="151.6" cy="-105" rx="55.49" ry="18"/>
<text text-anchor="middle" x="151.6" y="-101.3" font-family="Times,serif" font-size="14.00">Complete</text>
</g>
<!-- Returned&#45;&gt;Complete -->
<g id="edge12" class="edge">
<g id="edge14" class="edge">
<title>Returned&#45;&gt;Complete</title>
<path fill="none" stroke="black" d="M486.19,-86.8C486.19,-75.16 486.19,-59.55 486.19,-46.24"/>
<polygon fill="black" stroke="black" points="489.69,-46.18 486.19,-36.18 482.69,-46.18 489.69,-46.18"/>
<text text-anchor="middle" x="530.69" y="-57.8" font-family="Times,serif" font-size="14.00">exit_success</text>
</g>
<!-- Preempted&#45;&gt;Running_User -->
<g id="edge11" class="edge">
<title>Preempted&#45;&gt;Running_User</title>
<path fill="none" stroke="black" d="M328.79,-88.21C309.06,-74.9 280.62,-55.72 258.69,-40.92"/>
<polygon fill="black" stroke="black" points="260.58,-37.97 250.33,-35.28 256.66,-43.78 260.58,-37.97"/>
<path fill="none" stroke="black" d="M153.91,-173.93C153.4,-168.23 152.91,-161.85 152.6,-156 152.21,-148.66 151.97,-140.71 151.82,-133.35"/>
<polygon fill="black" stroke="black" points="155.32,-133.08 151.66,-123.13 148.32,-133.19 155.32,-133.08"/>
<text text-anchor="middle" x="197.1" y="-144.8" font-family="Times,serif" font-size="14.00">exit_success</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

@ -11,7 +11,7 @@ validate() {
declare -i major=0
declare -i minor=0
declare -i patch=0
declare -i required_major=9
declare -i required_major=11
declare -i required_minor=0
declare -i required_patch=0

@ -49,21 +49,21 @@ BINARY_NAME=sledgert
# CFLAGS += -DLOG_TO_FILE
# Various Informational Logs for Debugging
# CFLAGS += -DLOG_ADMISSIONS_CONTROL
# CFLAGS += -DLOG_CONTEXT_SWITCHES
# CFLAGS += -DLOG_HTTP_PARSER
# CFLAGS += -DLOG_LOCK_OVERHEAD
# CFLAGS += -DLOG_CONTEXT_SWITCHES
# CFLAGS += -DLOG_ADMISSIONS_CONTROL
# CFLAGS += -DLOG_REQUEST_ALLOCATION
# CFLAGS += -DLOG_PREEMPTION
# CFLAGS += -DLOG_MODULE_LOADING
# CFLAGS += -DLOG_PREEMPTION
# CFLAGS += -DLOG_REQUEST_ALLOCATION
# Stores and logs extended signal information for each worker
# CFLAGS += -DLOG_SOFTWARE_INTERRUPT_COUNTS
# This adds an array of sandbox states to all sandbox structs and appends states at each transition
# The history trucates when the number of elements equal SANDBOX_STATE_HISTORY_CAPACITY
# CFLAGS += -DLOG_STATE_CHANGES
# Stores the max number of deferred SIGALRMS for each worker
# CFLAGS += -DLOG_DEFERRED_SIGALRM_MAX
# This dumps per module *.csv files containing the cycle a sandbox has been in RUNNING when each
# page is allocated. This helps understand the relationship to memory allocation and execution time.
# CFLAGS += -DLOG_SANDBOX_MEMORY_PROFILE

@ -74,22 +74,16 @@ arch_context_restore_fast(mcontext_t *active_context, struct arch_context *sandb
* @param b - the registers and context of what we're switching to
* @return always returns 0, indicating success
*
* NULL in either of these values indicates the "no sandbox to execute" state,
* which defaults to resuming execution of main
*/
static inline int
arch_context_switch(struct arch_context *a, struct arch_context *b)
{
/* if both a and b are NULL, there is no state change */
assert(a != NULL || b != NULL);
assert(a != NULL);
assert(b != NULL);
/* Assumption: The caller does not switch to itself */
assert(a != b);
/* Set any NULLs to worker_thread_base_context to resume execution of main */
if (a == NULL) a = &worker_thread_base_context;
if (b == NULL) b = &worker_thread_base_context;
/* A Transition {Running} -> Fast */
assert(a->variant == ARCH_CONTEXT_VARIANT_RUNNING);

@ -0,0 +1,41 @@
#pragma once
#include <stdio.h>
#include <stdarg.h>
#define PRETTY_PRINT_COLOR_CODE_RED "\033[1;31m"
#define PRETTY_COLOR_CODE_GREEN "\033[0;32m"
#define PRETTY_PRINT_COLOR_CODE_RESET "\033[0m"
#define PRETTY_PRINT_GREEN_ENABLED PRETTY_COLOR_CODE_GREEN "Enabled" PRETTY_PRINT_COLOR_CODE_RESET
#define PRETTY_PRINT_RED_DISABLED PRETTY_PRINT_COLOR_CODE_RED "Disabled" PRETTY_PRINT_COLOR_CODE_RESET
#define PRETTY_PRINT_KEY_LEN 30
static inline void
pretty_print_key(char *heading)
{
printf("\t%-*s", PRETTY_PRINT_KEY_LEN, heading);
}
static inline void
pretty_print_key_value(char *key, char *fmt, ...)
{
va_list ap;
pretty_print_key(key);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
static inline void
pretty_print_key_enabled(char *key)
{
pretty_print_key_value(key, "%s\n", PRETTY_PRINT_GREEN_ENABLED);
}
static inline void
pretty_print_key_disabled(char *key)
{
pretty_print_key_value(key, "%s\n", PRETTY_PRINT_RED_DISABLED);
}

@ -8,6 +8,19 @@
#include "likely.h"
#include "types.h"
/**
* Optimizing compilers and modern CPUs reorder instructions however it sees fit. This means that the resulting
* execution order may differ from the order of our source code. If there is a variable protecting a critical section,
* this means that code may move out of or into the critical section, which could cause bugs. In order to protect
* against this, we need to improve an ordering contraint via a "memory barrier." Inline assembly acts as a such barrier
* that no assembly instructions can be reordered across. An example of how this is used in this code base is in ensure
* that code is either intentionally preemptable or non-preemptable.
*
* Wikipedia: https://en.wikipedia.org/wiki/Memory_barrier
* Linux: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/memory-barriers.txt
*/
#define barrier() __asm__ __volatile__("" ::: "memory")
#define RUNTIME_EXPECTED_EXECUTION_US_MAX 3600000000
#define RUNTIME_HTTP_REQUEST_SIZE_MAX 100000000 /* 100 MB */
#define RUNTIME_HTTP_RESPONSE_SIZE_MAX 100000000 /* 100 MB */

@ -59,12 +59,6 @@ sandbox_get_priority(void *element)
return sandbox->absolute_deadline;
};
static inline bool
sandbox_is_preemptable(struct sandbox *sandbox)
{
return sandbox && sandbox->state == SANDBOX_RUNNING_USER;
};
static inline void
sandbox_open_http(struct sandbox *sandbox)
{

@ -1,5 +1,6 @@
#pragma once
#include "pretty_print.h"
#include "runtime.h"
#include "sandbox_types.h"
@ -12,9 +13,9 @@ static inline void
sandbox_perf_log_print_header()
{
if (sandbox_perf_log == NULL) { perror("sandbox perf log"); }
fprintf(sandbox_perf_log,
"id,module,port,state,deadline,actual,queued,uninitialized,allocated,initialized,runnable,preempted,"
"running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory\n");
fprintf(sandbox_perf_log, "id,module,port,state,deadline,actual,queued,uninitialized,allocated,initialized,"
"runnable,interrupted,preempted,"
"running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory\n");
}
/**
@ -35,15 +36,16 @@ sandbox_perf_log_print_entry(struct sandbox *sandbox)
* becomes more intelligent, then peak linear memory size needs to be tracked
* seperately from current linear memory size.
*/
fprintf(sandbox_perf_log, "%lu,%s,%d,%s,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u,%u\n",
fprintf(sandbox_perf_log, "%lu,%s,%d,%s,%lu,%lu,%lu,,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u,%u\n",
sandbox->id, sandbox->module->name, sandbox->module->port, sandbox_state_stringify(sandbox->state),
sandbox->module->relative_deadline, sandbox->total_time, queued_duration,
sandbox->duration_of_state[SANDBOX_UNINITIALIZED], sandbox->duration_of_state[SANDBOX_ALLOCATED],
sandbox->duration_of_state[SANDBOX_INITIALIZED], sandbox->duration_of_state[SANDBOX_RUNNABLE],
sandbox->duration_of_state[SANDBOX_PREEMPTED], sandbox->duration_of_state[SANDBOX_RUNNING_SYS],
sandbox->duration_of_state[SANDBOX_RUNNING_USER], sandbox->duration_of_state[SANDBOX_ASLEEP],
sandbox->duration_of_state[SANDBOX_RETURNED], sandbox->duration_of_state[SANDBOX_COMPLETE],
sandbox->duration_of_state[SANDBOX_ERROR], runtime_processor_speed_MHz, sandbox->memory.size);
sandbox->duration_of_state[SANDBOX_INTERRUPTED], sandbox->duration_of_state[SANDBOX_PREEMPTED],
sandbox->duration_of_state[SANDBOX_RUNNING_SYS], sandbox->duration_of_state[SANDBOX_RUNNING_USER],
sandbox->duration_of_state[SANDBOX_ASLEEP], sandbox->duration_of_state[SANDBOX_RETURNED],
sandbox->duration_of_state[SANDBOX_COMPLETE], sandbox->duration_of_state[SANDBOX_ERROR],
runtime_processor_speed_MHz, sandbox->memory.size);
}
static inline void
@ -51,12 +53,12 @@ sandbox_perf_log_init()
{
char *sandbox_perf_log_path = getenv("SLEDGE_SANDBOX_PERF_LOG");
if (sandbox_perf_log_path != NULL) {
printf("\tSandbox Performance Log: %s\n", sandbox_perf_log_path);
pretty_print_key_value("Sandbox Performance Log", "%s\n", sandbox_perf_log_path);
sandbox_perf_log = fopen(sandbox_perf_log_path, "w");
if (sandbox_perf_log == NULL) perror("sandbox_perf_log_init\n");
sandbox_perf_log_print_header();
} else {
printf("\tSandbox Performance Log: Disabled\n");
pretty_print_key_disabled("Sandbox Performance Log");
}
}

@ -54,8 +54,7 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state)
sandbox_perf_log_print_entry(sandbox);
sandbox_summarize_page_allocations(sandbox);
/* Do not touch sandbox state after adding to completion queue to avoid use-after-free bugs */
local_completion_queue_add(sandbox);
/* Does not add to completion queue until in cooperative scheduler */
}
static inline void

@ -61,8 +61,7 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state)
sandbox_perf_log_print_entry(sandbox);
sandbox_summarize_page_allocations(sandbox);
/* Do not touch sandbox after adding to completion queue to avoid use-after-free bugs */
local_completion_queue_add(sandbox);
/* Does not add to completion queue until in cooperative scheduler */
}
static inline void

@ -0,0 +1,62 @@
#pragma once
#include <assert.h>
#include <stdint.h>
#include "arch/getcycles.h"
#include "current_sandbox.h"
#include "panic.h"
#include "sandbox_functions.h"
#include "sandbox_state_history.h"
#include "sandbox_types.h"
static inline void
sandbox_set_as_interrupted(struct sandbox *sandbox, sandbox_state_t last_state)
{
assert(sandbox);
/* WARNING: All code before this assignment is preemptable */
sandbox->state = SANDBOX_INTERRUPTED;
barrier();
uint64_t now = __getcycles();
/* State Change Bookkeeping */
sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change);
sandbox->timestamp_of.last_state_change = now;
/* We do not append SANDBOX_INTERRUPTED to the sandbox_state_history because it would quickly fill the buffer */
runtime_sandbox_total_increment(SANDBOX_INTERRUPTED);
runtime_sandbox_total_decrement(last_state);
}
static inline void
sandbox_interrupt(struct sandbox *sandbox)
{
sandbox_set_as_interrupted(sandbox, sandbox->state);
}
/**
* @brief Transition sandbox back to interrupted state
* @param sandbox
* @param interrupted_state - state to return to
*/
static inline void
sandbox_interrupt_return(struct sandbox *sandbox, sandbox_state_t interrupted_state)
{
assert(sandbox);
assert(interrupted_state != SANDBOX_INTERRUPTED);
uint64_t now = __getcycles();
/* State Change Bookkeeping */
sandbox->duration_of_state[SANDBOX_INTERRUPTED] += (now - sandbox->timestamp_of.last_state_change);
sandbox->timestamp_of.last_state_change = now;
/* We do not append SANDBOX_INTERRUPTED to the sandbox_state_history because it would quickly fill the buffer */
runtime_sandbox_total_increment(interrupted_state);
runtime_sandbox_total_decrement(SANDBOX_INTERRUPTED);
barrier();
/* WARNING: Code after this assignment may be preemptable */
sandbox->state = interrupted_state;
}

@ -26,8 +26,7 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state)
uint64_t now = __getcycles();
switch (last_state) {
case SANDBOX_RUNNING_SYS: {
current_sandbox_set(NULL);
case SANDBOX_INTERRUPTED: {
break;
}
default: {
@ -47,6 +46,6 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state)
static inline void
sandbox_preempt(struct sandbox *sandbox)
{
assert(sandbox->state == SANDBOX_RUNNING_SYS);
sandbox_set_as_preempted(sandbox, SANDBOX_RUNNING_SYS);
assert(sandbox->state == SANDBOX_INTERRUPTED);
sandbox_set_as_preempted(sandbox, SANDBOX_INTERRUPTED);
}

@ -14,8 +14,12 @@ static inline void
sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state)
{
assert(sandbox);
/* WARNING: All code before this assignment is preemptable if transitioning from RUNNING_USER */
sandbox->state = SANDBOX_RUNNING_SYS;
uint64_t now = __getcycles();
barrier();
uint64_t now = __getcycles();
switch (last_state) {
case SANDBOX_RUNNING_USER: {
@ -25,13 +29,10 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state)
}
case SANDBOX_RUNNABLE: {
assert(sandbox);
current_sandbox_set(sandbox);
/* Does not handle context switch because the caller knows if we need to use fast or slow switched. We
* can fix this by breakout out SANDBOX_RUNNABLE and SANDBOX_PREEMPTED */
break;
}
default: {
panic("Sandbox %lu | Illegal transition from %s to Running Kernel\n", sandbox->id,
panic("Sandbox %lu | Illegal transition from %s to Running Sys\n", sandbox->id,
sandbox_state_stringify(last_state));
}
}
@ -45,7 +46,7 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state)
}
static inline void
sandbox_interrupt(struct sandbox *sandbox)
sandbox_syscall(struct sandbox *sandbox)
{
assert(sandbox->state == SANDBOX_RUNNING_USER);
sandbox_set_as_running_sys(sandbox, SANDBOX_RUNNING_USER);

@ -24,8 +24,6 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state)
break;
}
case SANDBOX_PREEMPTED: {
assert(sandbox);
current_sandbox_set(sandbox);
break;
}
default: {
@ -42,9 +40,12 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state)
runtime_sandbox_total_increment(SANDBOX_RUNNING_USER);
runtime_sandbox_total_decrement(last_state);
/* WARNING: This state change needs to be at the end of this transition because all code below this assignment
* is preemptable */
barrier();
sandbox->state = SANDBOX_RUNNING_USER;
/* WARNING: All code after this assignment is preemptable */
/* Now that we are preemptable, we can replay deferred sigalrms */
software_interrupt_deferred_sigalrm_replay();
}
static inline void

@ -15,6 +15,7 @@ typedef enum
SANDBOX_PREEMPTED,
SANDBOX_RUNNING_SYS,
SANDBOX_RUNNING_USER,
SANDBOX_INTERRUPTED,
SANDBOX_ASLEEP,
SANDBOX_RETURNED,
SANDBOX_COMPLETE,

@ -19,9 +19,49 @@
#include "sandbox_set_as_preempted.h"
#include "sandbox_set_as_runnable.h"
#include "sandbox_set_as_running_sys.h"
#include "sandbox_set_as_interrupted.h"
#include "sandbox_set_as_running_user.h"
#include "scheduler_execute_epoll_loop.h"
/**
* This scheduler provides for cooperative and preemptive multitasking in a OS process's userspace.
*
* When executing cooperatively, the scheduler is directly invoked via `scheduler_cooperative_sched`. It runs a single
* time in the existing context in order to try to execute a direct sandbox-to-sandbox switch. When no sandboxes are
* available to execute, the scheduler executes a context switch to `worker_thread_base_context`, which calls
* `scheduler_cooperative_sched` in an infinite idle loop. If the scheduler needs to restore a sandbox that was
* previously preempted, it raises a SIGUSR1 signal to enter the scheduler handler to be able to restore the full
* mcontext structure saved during the last preemption. Otherwise, the cooperative scheduler triggers a "fast switch",
* which only updates the instruction and stack pointer.
*
* Preemptive scheduler is provided by POSIX timers using a set interval defining a scheduling quantum. Our signal
* handler is configured to mask nested signals. Given that POSIX specifies that the kernel only delivers a SIGALRM to a
* single thread, the lucky thread that receives the kernel thread has the responsibility of propagating this signal
* onto all other worker threads. This must occur even when a worker thread is running a sandbox in a nonpreemptable
* state.
*
* When a SIGALRM fires, a worker can be in one of four states:
*
* 1) "Running a signal handler" - We mask signals when we are executing a signal handler, which results in signals
* being ignored. A kernel signal should get delivered to another unmasked worker, so propagation still occurs.
*
* 2) "Running the Cooperative Scheduler" - This is signified by the thread local current_sandbox being set to NULL. We
* propagate the signal and return immediately because we know we're already in the scheduler. We have no sandboxes to
* interrupt, so no sandbox state transitions occur.
*
* 3) "Running a Sandbox in a state other than SANDBOX_RUNNING_USER" - We call sandbox_interrupt on current_sandbox,
* propagate the sigalrms to the other workers, defer the sigalrm locally, and then return. The SANDBOX_INTERRUPTED
* timekeeping data is increased to account for the time needed to propagate the sigalrms.
*
* 4) "Running a Sandbox in the SANDBOX_RUNNING_USER state - We call sandbox_interrupt on current_sandbox, propagate
* the sigalrms to the other workers, and then actually enter the scheduler via scheduler_preemptive_sched. The
* interrupted sandbox may either be preempted or return to depending on the scheduler. If preempted, the interrupted
* mcontext is saved to the sandbox structure. The SANDBOX_INTERRUPTED timekeeping data is increased to account for the
* time needed to propagate the sigalrms, run epoll, query the scheduler data structure, and (potentially) allocate and
* initialize a sandbox.
*/
enum SCHEDULER
{
SCHEDULER_FIFO = 0,
@ -100,14 +140,6 @@ err:
static inline struct sandbox *
scheduler_get_next()
{
#ifdef LOG_DEFERRED_SIGALRM_MAX
if (unlikely(software_interrupt_deferred_sigalrm
> software_interrupt_deferred_sigalrm_max[worker_thread_idx])) {
software_interrupt_deferred_sigalrm_max[worker_thread_idx] = software_interrupt_deferred_sigalrm;
}
#endif
atomic_store(&software_interrupt_deferred_sigalrm, 0);
switch (scheduler) {
case SCHEDULER_EDF:
return scheduler_edf_get_next();
@ -188,12 +220,14 @@ scheduler_preemptive_switch_to(ucontext_t *interrupted_context, struct sandbox *
case ARCH_CONTEXT_VARIANT_FAST: {
assert(next->state == SANDBOX_RUNNABLE);
arch_context_restore_fast(&interrupted_context->uc_mcontext, &next->ctxt);
current_sandbox_set(next);
sandbox_set_as_running_sys(next, SANDBOX_RUNNABLE);
break;
}
case ARCH_CONTEXT_VARIANT_SLOW: {
assert(next->state == SANDBOX_PREEMPTED);
arch_context_restore_slow(&interrupted_context->uc_mcontext, &next->ctxt);
current_sandbox_set(next);
sandbox_set_as_running_user(next, SANDBOX_PREEMPTED);
break;
}
@ -218,32 +252,28 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context)
/* Process epoll to make sure that all runnable jobs are considered for execution */
scheduler_execute_epoll_loop();
struct sandbox *current = current_sandbox_get();
assert(current != NULL);
assert(current->state == SANDBOX_RUNNING_USER);
sandbox_interrupt(current);
struct sandbox *interrupted_sandbox = current_sandbox_get();
assert(interrupted_sandbox != NULL);
assert(interrupted_sandbox->state == SANDBOX_INTERRUPTED);
struct sandbox *next = scheduler_get_next();
/* Assumption: the current sandbox is on the runqueue, so the scheduler should always return something */
assert(next != NULL);
/* If current equals next, no switch is necessary, so resume execution */
if (current == next) {
sandbox_return(current);
if (interrupted_sandbox == next) {
sandbox_interrupt_return(interrupted_sandbox, SANDBOX_RUNNING_USER);
return;
}
#ifdef LOG_PREEMPTION
debuglog("Preempting sandbox %lu to run sandbox %lu\n", current->id, next->id);
debuglog("Preempting sandbox %lu to run sandbox %lu\n", interrupted_sandbox->id, next->id);
#endif
scheduler_log_sandbox_switch(current, next);
/* Preempt executing sandbox */
sandbox_preempt(current);
arch_context_save_slow(&current->ctxt, &interrupted_context->uc_mcontext);
scheduler_log_sandbox_switch(interrupted_sandbox, next);
sandbox_preempt(interrupted_sandbox);
arch_context_save_slow(&interrupted_sandbox->ctxt, &interrupted_context->uc_mcontext);
scheduler_preemptive_switch_to(interrupted_context, next);
}
@ -253,25 +283,24 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context)
* @param next_sandbox The Sandbox to switch to
*/
static inline void
scheduler_cooperative_switch_to(struct sandbox *next_sandbox)
scheduler_cooperative_switch_to(struct arch_context *current_context, struct sandbox *next_sandbox)
{
assert(current_sandbox_get() == NULL);
struct arch_context *next_context = &next_sandbox->ctxt;
scheduler_log_sandbox_switch(NULL, next_sandbox);
/* Switch to next sandbox */
switch (next_sandbox->state) {
case SANDBOX_RUNNABLE: {
assert(next_context->variant == ARCH_CONTEXT_VARIANT_FAST);
current_sandbox_set(next_sandbox);
sandbox_set_as_running_sys(next_sandbox, SANDBOX_RUNNABLE);
break;
}
case SANDBOX_PREEMPTED: {
assert(next_context->variant == ARCH_CONTEXT_VARIANT_SLOW);
/* arch_context_switch triggers a SIGUSR1, which transitions next_sandbox to running_user */
current_sandbox_set(next_sandbox);
/* arch_context_switch triggers a SIGUSR1, which transitions next_sandbox to running_user */
break;
}
default: {
@ -279,28 +308,95 @@ scheduler_cooperative_switch_to(struct sandbox *next_sandbox)
sandbox_state_stringify(next_sandbox->state));
}
}
arch_context_switch(current_context, next_context);
}
arch_context_switch(&worker_thread_base_context, next_context);
static inline void
scheduler_switch_to_base_context(struct arch_context *current_context)
{
/* Assumption: Base Worker context should never be preempted */
assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST);
arch_context_switch(current_context, &worker_thread_base_context);
}
/* A sandbox cannot execute the scheduler directly. It must yield to the base context, and then the context calls this
* within its idle loop
/* The idle_loop is executed by the base_context. This should not be called directly */
static inline void
scheduler_idle_loop()
{
while (true) {
/* Assumption: only called by the "base context" */
assert(current_sandbox_get() == NULL);
/* Deferred signals should have been cleared by this point */
assert(deferred_sigalrm == 0);
/* Try to wakeup sleeping sandboxes */
scheduler_execute_epoll_loop();
/* Switch to a sandbox if one is ready to run */
struct sandbox *next_sandbox = scheduler_get_next();
if (next_sandbox != NULL) {
scheduler_cooperative_switch_to(&worker_thread_base_context, next_sandbox);
}
/* Clear the completion queue */
local_completion_queue_free();
}
}
/**
* @brief Used to cooperative switch sandboxes when a sandbox sleeps or exits
* Because of use-after-free bugs that interfere with our loggers, when a sandbox exits and switches away never to
* return, the boolean add_to_completion_queue needs to be set to true. Otherwise, we will leak sandboxes.
* @param add_to_completion_queue - Indicates that the sandbox should be added to the completion queue before switching
* away
*/
static inline void
scheduler_cooperative_sched()
scheduler_cooperative_sched(bool add_to_completion_queue)
{
/* Assumption: only called by the "base context" */
struct sandbox *exiting_sandbox = current_sandbox_get();
assert(exiting_sandbox != NULL);
/* Clearing current sandbox indicates we are entering the cooperative scheduler */
current_sandbox_set(NULL);
barrier();
software_interrupt_deferred_sigalrm_clear();
struct arch_context *exiting_context = &exiting_sandbox->ctxt;
/* Assumption: Called by an exiting or sleeping sandbox */
assert(current_sandbox_get() == NULL);
/* Deferred signals should have been cleared by this point */
assert(deferred_sigalrm == 0);
/* Try to wakeup sleeping sandboxes */
scheduler_execute_epoll_loop();
/* We have not added ourself to the completion queue, so we can free */
local_completion_queue_free();
/* Switch to a sandbox if one is ready to run */
struct sandbox *next_sandbox = scheduler_get_next();
if (next_sandbox != NULL) scheduler_cooperative_switch_to(next_sandbox);
/* Clear the completion queue */
local_completion_queue_free();
/* If our sandbox slept and immediately woke up, we can just return */
if (next_sandbox == exiting_sandbox) {
sandbox_set_as_running_sys(next_sandbox, SANDBOX_RUNNABLE);
current_sandbox_set(next_sandbox);
return;
}
scheduler_log_sandbox_switch(exiting_sandbox, next_sandbox);
if (add_to_completion_queue) local_completion_queue_add(exiting_sandbox);
/* Do not touch sandbox struct after this point! */
if (next_sandbox != NULL) {
scheduler_cooperative_switch_to(exiting_context, next_sandbox);
} else {
scheduler_switch_to_base_context(exiting_context);
}
}

@ -5,6 +5,7 @@
#include <panic.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -12,15 +13,9 @@
#include "debuglog.h"
#include "runtime.h"
#include "software_interrupt_counts.h"
#include "worker_thread.h"
/************
* Externs *
***********/
extern _Atomic thread_local volatile sig_atomic_t software_interrupt_deferred_sigalrm;
extern _Atomic volatile sig_atomic_t * software_interrupt_deferred_sigalrm_max;
/*************************
* Public Static Inlines *
************************/
@ -74,13 +69,30 @@ software_interrupt_unmask_signal(int signal)
return 0;
}
extern thread_local _Atomic volatile sig_atomic_t deferred_sigalrm;
static inline void
software_interrupt_deferred_sigalrm_clear()
{
software_interrupt_counts_deferred_sigalrm_max_update(deferred_sigalrm);
atomic_store(&deferred_sigalrm, 0);
}
static inline void
software_interrupt_deferred_sigalrm_replay()
{
if (deferred_sigalrm > 0) {
software_interrupt_deferred_sigalrm_clear();
software_interrupt_counts_deferred_sigalrm_replay_increment();
raise(SIGALRM);
}
}
/*************************
* Exports from module.c *
* Exports from software_interrupt.c *
************************/
void software_interrupt_initialize(void);
void software_interrupt_arm_timer(void);
void software_interrupt_cleanup(void);
void software_interrupt_disarm_timer(void);
void software_interrupt_set_interval_duration(uint64_t cycles);
void software_interrupt_deferred_sigalrm_max_free(void);
void software_interrupt_deferred_sigalrm_max_print(void);
void software_interrupt_initialize(void);

@ -0,0 +1,80 @@
#pragma once
#include <stdatomic.h>
#include <signal.h>
#include <stdlib.h>
#include "worker_thread.h"
extern _Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_max;
extern _Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_replay;
extern _Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_kernel;
extern _Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_thread;
extern _Atomic volatile sig_atomic_t *software_interrupt_counts_sigusr;
static inline void
software_interrupt_counts_alloc()
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
software_interrupt_counts_deferred_sigalrm_max = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t));
software_interrupt_counts_deferred_sigalrm_replay = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t));
software_interrupt_counts_sigalrm_kernel = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t));
software_interrupt_counts_sigalrm_thread = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t));
software_interrupt_counts_sigusr = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t));
#endif
}
static inline void
software_interrupt_counts_free()
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
free((void *)software_interrupt_counts_deferred_sigalrm_max);
free((void *)software_interrupt_counts_sigalrm_kernel);
free((void *)software_interrupt_counts_sigalrm_thread);
free((void *)software_interrupt_counts_sigusr);
#endif
}
static inline void
software_interrupt_counts_deferred_sigalrm_max_update(int deferred_sigalrm_count)
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
if (unlikely(deferred_sigalrm_count > software_interrupt_counts_deferred_sigalrm_max[worker_thread_idx])) {
software_interrupt_counts_deferred_sigalrm_max[worker_thread_idx] = deferred_sigalrm_count;
}
#endif
}
static inline void
software_interrupt_counts_deferred_sigalrm_replay_increment()
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
atomic_fetch_add(&software_interrupt_counts_deferred_sigalrm_replay[worker_thread_idx], 1);
#endif
}
static inline void
software_interrupt_counts_sigalrm_kernel_increment()
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
atomic_fetch_add(&software_interrupt_counts_sigalrm_kernel[worker_thread_idx], 1);
#endif
}
static inline void
software_interrupt_counts_sigalrm_thread_increment()
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
atomic_fetch_add(&software_interrupt_counts_sigalrm_thread[worker_thread_idx], 1);
#endif
}
static inline void
software_interrupt_counts_sigusr_increment()
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
atomic_fetch_add(&software_interrupt_counts_sigusr[worker_thread_idx], 1);
#endif
}
extern void software_interrupt_counts_log();

@ -33,30 +33,22 @@ thread_local struct sandbox_context_cache local_sandbox_context_cache = {
void
current_sandbox_sleep()
{
struct sandbox *sandbox = current_sandbox_get();
assert(sandbox != NULL);
struct arch_context *current_context = &sandbox->ctxt;
struct sandbox *sleeping_sandbox = current_sandbox_get();
assert(sleeping_sandbox != NULL);
scheduler_log_sandbox_switch(sandbox, NULL);
generic_thread_dump_lock_overhead();
assert(sandbox != NULL);
switch (sandbox->state) {
switch (sleeping_sandbox->state) {
case SANDBOX_RUNNING_SYS: {
sandbox_sleep(sandbox);
sandbox_sleep(sleeping_sandbox);
break;
}
default:
panic("Cooperatively switching from a sandbox in a non-terminal %s state\n",
sandbox_state_stringify(sandbox->state));
sandbox_state_stringify(sleeping_sandbox->state));
}
current_sandbox_set(NULL);
/* Assumption: Base Worker context should never be preempted */
assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST);
arch_context_switch(current_context, &worker_thread_base_context);
scheduler_cooperative_sched(false);
}
/**
@ -67,35 +59,26 @@ current_sandbox_sleep()
void
current_sandbox_exit()
{
struct sandbox *sandbox = current_sandbox_get();
current_sandbox_set(NULL);
struct sandbox *exiting_sandbox = current_sandbox_get();
assert(exiting_sandbox != NULL);
assert(sandbox != NULL);
struct arch_context *current_context = &sandbox->ctxt;
scheduler_log_sandbox_switch(sandbox, NULL);
generic_thread_dump_lock_overhead();
assert(sandbox != NULL);
switch (sandbox->state) {
switch (exiting_sandbox->state) {
case SANDBOX_RETURNED:
sandbox_exit_success(sandbox);
sandbox_exit_success(exiting_sandbox);
break;
case SANDBOX_RUNNING_SYS:
sandbox_exit_error(sandbox);
sandbox_exit_error(exiting_sandbox);
break;
default:
panic("Cooperatively switching from a sandbox in a non-terminal %s state\n",
sandbox_state_stringify(sandbox->state));
sandbox_state_stringify(exiting_sandbox->state));
}
/* Do not access sandbox after this, as it is on the completion queue! */
/* Assumption: Base Worker context should never be preempted */
assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST);
arch_context_switch(current_context, &worker_thread_base_context);
scheduler_cooperative_sched(true);
/* The schduler should never switch back to completed sandboxes */
/* The scheduler should never switch back to completed sandboxes */
assert(0);
}
@ -134,6 +117,8 @@ err:
generic_thread_dump_lock_overhead();
current_sandbox_exit();
assert(0);
return NULL;
}
static inline void
@ -143,7 +128,7 @@ current_sandbox_fini()
assert(sandbox != NULL);
char *error_message = "";
sandbox_interrupt(sandbox);
sandbox_syscall(sandbox);
sandbox->timestamp_of.completion = __getcycles();

@ -60,8 +60,7 @@ local_runqueue_list_rotate()
if (ps_list_head_one_node(&local_runqueue_list)) return;
struct sandbox *sandbox_at_head = local_runqueue_list_remove_and_return();
assert(sandbox_at_head->state == SANDBOX_RUNNING_SYS || sandbox_at_head->state == SANDBOX_RUNNABLE
|| sandbox_at_head->state == SANDBOX_PREEMPTED);
assert(sandbox_at_head->state == SANDBOX_INTERRUPTED);
local_runqueue_list_append(sandbox_at_head);
}

@ -14,6 +14,7 @@
#include <sys/fcntl.h>
#endif
#include "pretty_print.h"
#include "debuglog.h"
#include "listener_thread.h"
#include "module.h"
@ -58,7 +59,8 @@ runtime_allocate_available_cores()
/* Find the number of processors currently online */
runtime_total_online_processors = sysconf(_SC_NPROCESSORS_ONLN);
printf("\tCore Count (Online): %u\n", runtime_total_online_processors);
pretty_print_key_value("Core Count (Online)", "%u\n", runtime_total_online_processors);
/* If more than two cores are available, leave core 0 free to run OS tasks */
if (runtime_total_online_processors > 2) {
@ -84,9 +86,9 @@ runtime_allocate_available_cores()
runtime_worker_threads_count = max_possible_workers;
}
printf("\tListener core ID: %u\n", LISTENER_THREAD_CORE_ID);
printf("\tFirst Worker core ID: %u\n", runtime_first_worker_processor);
printf("\tWorker core count: %u\n", runtime_worker_threads_count);
pretty_print_key_value("Listener core ID", "%u\n", LISTENER_THREAD_CORE_ID);
pretty_print_key_value("First Worker core ID", "%u\n", runtime_first_worker_processor);
pretty_print_key_value("Worker core count", "%u\n", runtime_worker_threads_count);
}
/**
@ -184,7 +186,7 @@ runtime_configure()
} else {
panic("Invalid scheduler policy: %s. Must be {EDF|FIFO}\n", scheduler_policy);
}
printf("\tScheduler Policy: %s\n", scheduler_print(scheduler));
pretty_print_key_value("Scheduler Policy", "%s\n", scheduler_print(scheduler));
/* Sigalrm Handler Technique */
char *sigalrm_policy = getenv("SLEDGE_SIGALRM_HANDLER");
@ -197,12 +199,13 @@ runtime_configure()
} else {
panic("Invalid sigalrm policy: %s. Must be {BROADCAST|TRIAGED}\n", sigalrm_policy);
}
printf("\tSigalrm Policy: %s\n", runtime_print_sigalrm_handler(runtime_sigalrm_handler));
pretty_print_key_value("Sigalrm Policy", "%s\n", runtime_print_sigalrm_handler(runtime_sigalrm_handler));
/* Runtime Preemption Toggle */
char *preempt_disable = getenv("SLEDGE_DISABLE_PREEMPTION");
if (preempt_disable != NULL && strcmp(preempt_disable, "false") != 0) runtime_preemption_enabled = false;
printf("\tPreemption: %s\n", runtime_preemption_enabled ? "Enabled" : "Disabled");
pretty_print_key_value("Preemption", "%s\n",
runtime_preemption_enabled ? PRETTY_PRINT_GREEN_ENABLED : PRETTY_PRINT_RED_DISABLED);
/* Runtime Quantum */
char *quantum_raw = getenv("SLEDGE_QUANTUM_US");
@ -213,7 +216,7 @@ runtime_configure()
panic("SLEDGE_QUANTUM_US must be less than 999999 ms, saw %ld\n", quantum);
runtime_quantum_us = (uint32_t)quantum;
}
printf("\tQuantum: %u us\n", runtime_quantum_us);
pretty_print_key_value("Quantum", "%u us\n", runtime_quantum_us);
sandbox_perf_log_init();
}
@ -221,97 +224,113 @@ runtime_configure()
void
log_compiletime_config()
{
printf("Static Compiler Flags:\n");
/* System Stuff */
printf("System Flags:\n");
pretty_print_key("Architecture");
#if defined(aarch64)
printf("aarch64\n");
#elif defined(x86_64)
printf("x86_64\n");
#endif
pretty_print_key_value("Page Size", "%lu\n", PAGE_SIZE);
/* Feature Toggles */
printf("Static Compiler Flags (Features):\n");
#ifdef ADMISSIONS_CONTROL
printf("\tAdmissions Control: Enabled\n");
pretty_print_key_enabled("Admissions Control");
#else
printf("\tAdmissions Control: Disabled\n");
pretty_print_key_disabled("Admissions Control");
#endif
#ifdef NDEBUG
printf("\tAssertions and Debug Logs: Disabled\n");
/* Debugging Flags */
printf("Static Compiler Flags (Debugging):\n");
#ifndef NDEBUG
pretty_print_key_enabled("Assertions and Debug Logs");
#else
printf("\tAssertions and Debug Logs: Enabled\n");
pretty_print_key_disabled("Assertions and Debug Logs");
#endif
pretty_print_key("Logging to");
#ifdef LOG_TO_FILE
printf("\tLogging to: %s\n", RUNTIME_LOG_FILE);
printf("%s\n", RUNTIME_LOG_FILE);
#else
printf("\tLogging to: STDOUT and STDERR\n");
printf("STDOUT and STDERR\n");
#endif
#if defined(aarch64)
printf("\tArchitecture: %s\n", "aarch64");
#elif defined(x86_64)
printf("\tArchitecture: %s\n", "x86_64");
#ifdef LOG_ADMISSIONS_CONTROL
pretty_print_key_enabled("Log Admissions Control");
#else
pretty_print_key_disabled("Log Admissions Control");
#endif
printf("\tPage Size: %lu\n", PAGE_SIZE);
#ifdef LOG_HTTP_PARSER
printf("\tLog HTTP Parser: Enabled\n");
#ifdef LOG_CONTEXT_SWITCHES
pretty_print_key_enabled("Log Context Switches");
#else
printf("\tLog HTTP Parser: Disabled\n");
pretty_print_key_disabled("Log Context Switches");
#endif
#ifdef LOG_STATE_CHANGES
printf("\tLog State Changes: Enabled\n");
#ifdef LOG_HTTP_PARSER
pretty_print_key_enabled("Log HTTP Parser");
#else
printf("\tLog State Changes: Disabled\n");
pretty_print_key_disabled("Log HTTP Parser");
#endif
#ifdef LOG_LOCK_OVERHEAD
printf("\tLog Lock Overhead: Enabled\n");
pretty_print_key_enabled("Log Lock Overhead");
#else
printf("\tLog Lock Overhead: Disabled\n");
pretty_print_key_disabled("Log Lock Overhead");
#endif
#ifdef LOG_CONTEXT_SWITCHES
printf("\tLog Context Switches: Enabled\n");
#ifdef LOG_MODULE_LOADING
pretty_print_key_enabled("Log Module Loading");
#else
printf("\tLog Context Switches: Disabled\n");
pretty_print_key_disabled("Log Module Loading");
#endif
#ifdef LOG_ADMISSIONS_CONTROL
printf("\tLog Admissions Control: Enabled\n");
#ifdef LOG_PREEMPTION
pretty_print_key_enabled("Log Preemption");
#else
printf("\tLog Admissions Control: Disabled\n");
pretty_print_key_disabled("Log Preemption");
#endif
#ifdef LOG_REQUEST_ALLOCATION
printf("\tLog Request Allocation: Enabled\n");
pretty_print_key_enabled("Log Request Allocation");
#else
printf("\tLog Request Allocation: Disabled\n");
pretty_print_key_disabled("Log Request Allocation");
#endif
#ifdef LOG_PREEMPTION
printf("\tLog Preemption: Enabled\n");
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
pretty_print_key_enabled("Log Software Interrupt Counts");
#else
printf("\tLog Preemption: Disabled\n");
pretty_print_key_disabled("Log Software Interrupt Counts");
#endif
#ifdef LOG_MODULE_LOADING
printf("\tLog Module Loading: Enabled\n");
#ifdef LOG_STATE_CHANGES
pretty_print_key_enabled("Log State Changes");
#else
printf("\tLog Module Loading: Disabled\n");
pretty_print_key_disabled("Log State Changes");
#endif
#ifdef LOG_TOTAL_REQS_RESPS
printf("\tLog Total Reqs/Resps: Enabled\n");
pretty_print_key_enabled("Log Total Reqs/Resps");
#else
printf("\tLog Total Reqs/Resps: Disabled\n");
pretty_print_key_disabled("Log Total Reqs/Resps");
#endif
#ifdef LOG_SANDBOX_COUNT
printf("\tLog Sandbox Count: Enabled\n");
pretty_print_key_enabled("Log Sandbox Count");
#else
printf("\tLog Sandbox Count: Disabled\n");
pretty_print_key_disabled("Log Sandbox Count");
#endif
#ifdef LOG_LOCAL_RUNQUEUE
printf("\tLog Local Runqueue: Enabled\n");
pretty_print_key_enabled("Log Local Runqueue");
#else
printf("\tLog Local Runqueue: Disabled\n");
pretty_print_key_disabled("Log Local Runqueue");
#endif
}
@ -333,9 +352,9 @@ main(int argc, char **argv)
runtime_processor_speed_MHz = runtime_get_processor_speed_MHz();
if (unlikely(runtime_processor_speed_MHz == 0)) panic("Failed to detect processor speed\n");
software_interrupt_set_interval_duration(runtime_quantum_us * runtime_processor_speed_MHz);
int heading_length = 30;
printf("\tProcessor Speed: %u MHz\n", runtime_processor_speed_MHz);
pretty_print_key_value("Processor Speed", "%u MHz\n", runtime_processor_speed_MHz);
runtime_set_resource_limits_to_max();
runtime_allocate_available_cores();

@ -46,8 +46,7 @@ runtime_cleanup()
if (runtime_worker_threads_argument) free(runtime_worker_threads_argument);
if (runtime_worker_threads) free(runtime_worker_threads);
software_interrupt_deferred_sigalrm_max_print();
software_interrupt_deferred_sigalrm_max_free();
software_interrupt_cleanup();
exit(EXIT_SUCCESS);
}
@ -81,11 +80,11 @@ runtime_set_resource_limits_to_max()
snprintf(max, uint64_t_max_digits, "%lu", limit.rlim_max);
}
if (limit.rlim_cur == limit.rlim_max) {
printf("\t%s: %s\n", resource_names[i], max);
pretty_print_key_value(resource_names[i], "%s\n", max);
} else {
limit.rlim_cur = limit.rlim_max;
if (setrlimit(resource, &limit) < 0) panic_err();
printf("\t%s: %s (Increased from %s)\n", resource_names[i], max, lim);
pretty_print_key_value(resource_names[i], "%s (Increased from %s)\n", max, lim);
}
}
}

@ -13,6 +13,7 @@ const char *sandbox_state_labels[SANDBOX_STATE_COUNT] = {
[SANDBOX_ALLOCATED] = "Allocated",
[SANDBOX_INITIALIZED] = "Initialized",
[SANDBOX_RUNNABLE] = "Runnable",
[SANDBOX_INTERRUPTED] = "Interrupted",
[SANDBOX_PREEMPTED] = "Preempted",
[SANDBOX_RUNNING_SYS] = "Running Sys",
[SANDBOX_RUNNING_USER] = "Running User",

@ -3,6 +3,7 @@
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
#include <threads.h>
@ -18,55 +19,14 @@
#include "panic.h"
#include "runtime.h"
#include "sandbox_set_as_running_user.h"
#include "sandbox_set_as_interrupted.h"
#include "sandbox_types.h"
#include "scheduler.h"
#include "software_interrupt.h"
#include "software_interrupt_counts.h"
/*******************
* Process Globals *
******************/
static uint64_t software_interrupt_interval_duration_in_cycles;
/******************
* Thread Globals *
*****************/
thread_local _Atomic static volatile sig_atomic_t software_interrupt_SIGALRM_kernel_count = 0;
thread_local _Atomic static volatile sig_atomic_t software_interrupt_SIGALRM_thread_count = 0;
thread_local _Atomic static volatile sig_atomic_t software_interrupt_SIGUSR_count = 0;
thread_local _Atomic volatile sig_atomic_t software_interrupt_deferred_sigalrm = 0;
thread_local _Atomic volatile sig_atomic_t software_interrupt_signal_depth = 0;
_Atomic volatile sig_atomic_t *software_interrupt_deferred_sigalrm_max;
void
software_interrupt_deferred_sigalrm_max_alloc()
{
#ifdef LOG_DEFERRED_SIGALRM_MAX
software_interrupt_deferred_sigalrm_max = calloc(runtime_worker_threads_count, sizeof(_Atomic(sig_atomic_t)));
#endif
}
void
software_interrupt_deferred_sigalrm_max_free()
{
#ifdef LOG_DEFERRED_SIGALRM_MAX
if (software_interrupt_deferred_sigalrm_max) free((void *)software_interrupt_deferred_sigalrm_max);
#endif
}
void
software_interrupt_deferred_sigalrm_max_print()
{
#ifdef LOG_DEFERRED_SIGALRM_MAX
printf("Max Deferred Sigalrms\n");
for (int i = 0; i < runtime_worker_threads_count; i++) {
printf("Worker %d: %d\n", i, software_interrupt_deferred_sigalrm_max[i]);
}
fflush(stdout);
#endif
}
thread_local _Atomic volatile sig_atomic_t handler_depth = 0;
thread_local _Atomic volatile sig_atomic_t deferred_sigalrm = 0;
/***************************************
* Externs
@ -83,11 +43,11 @@ extern pthread_t *runtime_worker_threads;
* This function broadcasts the sigalarm signal to all other worker threads
*/
static inline void
sigalrm_propagate_workers(siginfo_t *signal_info)
propagate_sigalrm(siginfo_t *signal_info)
{
/* Signal was sent directly by the kernel, so forward to other threads */
if (signal_info->si_code == SI_KERNEL) {
atomic_fetch_add(&software_interrupt_SIGALRM_kernel_count, 1);
software_interrupt_counts_sigalrm_kernel_increment();
for (int i = 0; i < runtime_worker_threads_count; i++) {
/* All threads should have been initialized */
assert(runtime_worker_threads[i] != 0);
@ -109,21 +69,24 @@ sigalrm_propagate_workers(siginfo_t *signal_info)
}
}
} else {
atomic_fetch_add(&software_interrupt_SIGALRM_thread_count, 1);
software_interrupt_counts_sigalrm_thread_increment();
/* Signal forwarded from another thread. Just confirm it resulted from pthread_kill */
assert(signal_info->si_code == SI_TKILL);
}
}
/**
* Validates that the thread running the signal handler is a known worker thread
*/
static inline void
software_interrupt_validate_worker()
static inline bool
worker_thread_is_running_cooperative_scheduler(void)
{
#ifndef NDEBUG
if (listener_thread_is_running()) panic("The listener thread unexpectedly received a signal!");
#endif
return current_sandbox_get() == NULL;
}
static inline bool
current_sandbox_is_preemptable()
{
struct sandbox *sandbox = current_sandbox_get();
return sandbox != NULL && sandbox->state == SANDBOX_RUNNING_USER;
}
/**
@ -144,55 +107,67 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void
assert(runtime_preemption_enabled);
/* Signals should not nest */
assert(software_interrupt_signal_depth == 0);
atomic_fetch_add(&software_interrupt_signal_depth, 1);
assert(handler_depth == 0);
atomic_fetch_add(&handler_depth, 1);
ucontext_t * interrupted_context = (ucontext_t *)interrupted_context_raw;
struct sandbox *current_sandbox = current_sandbox_get();
switch (signal_type) {
case SIGALRM: {
sigalrm_propagate_workers(signal_info);
/* Nonpreemptive, so defer */
if (!sandbox_is_preemptable(current_sandbox)) {
atomic_fetch_add(&software_interrupt_deferred_sigalrm, 1);
goto done;
if (worker_thread_is_running_cooperative_scheduler()) {
/* There is no benefit to deferring SIGALRMs that occur when we are already in the cooperative
* scheduler, so just propagate and return */
propagate_sigalrm(signal_info);
} else if (current_sandbox_is_preemptable()) {
/* Preemptable, so run scheduler. The scheduler handles outgoing state changes */
sandbox_interrupt(current_sandbox);
propagate_sigalrm(signal_info);
scheduler_preemptive_sched(interrupted_context);
} else {
/* We transition the sandbox to an interrupted state to exclude time propagating signals and
* running the scheduler from per-sandbox accounting */
sandbox_state_t interrupted_state = current_sandbox->state;
sandbox_interrupt(current_sandbox);
propagate_sigalrm(signal_info);
atomic_fetch_add(&deferred_sigalrm, 1);
sandbox_interrupt_return(current_sandbox, interrupted_state);
}
scheduler_preemptive_sched(interrupted_context);
goto done;
break;
}
case SIGUSR1: {
assert(current_sandbox);
assert(current_sandbox->state == SANDBOX_PREEMPTED);
assert(current_sandbox->ctxt.variant == ARCH_CONTEXT_VARIANT_SLOW);
atomic_fetch_add(&software_interrupt_SIGUSR_count, 1);
software_interrupt_counts_sigusr_increment();
#ifdef LOG_PREEMPTION
debuglog("Total SIGUSR1 Received: %d\n", software_interrupt_SIGUSR_count);
debuglog("Restoring sandbox: %lu, Stack %llu\n", current_sandbox->id,
current_sandbox->ctxt.mctx.gregs[REG_RSP]);
#endif
/* It is the responsibility of the caller to invoke current_sandbox_set before triggering the SIGUSR1 */
scheduler_preemptive_switch_to(interrupted_context, current_sandbox);
goto done;
break;
}
default: {
const char *signal_name = strsignal(signal_type);
switch (signal_info->si_code) {
case SI_TKILL:
panic("Unexpectedly received signal %d from a thread kill, but we have no handler\n",
signal_type);
panic("software_interrupt_handle_signals unexpectedly received signal %s from a thread kill\n",
signal_name);
case SI_KERNEL:
panic("Unexpectedly received signal %d from the kernel, but we have no handler\n", signal_type);
panic("software_interrupt_handle_signals unexpectedly received signal %s from the kernel\n",
signal_name);
default:
panic("Anomolous Signal\n");
panic("software_interrupt_handle_signals unexpectedly received signal %s with si_code %d\n",
signal_name, signal_info->si_code);
}
}
}
done:
atomic_fetch_sub(&software_interrupt_signal_depth, 1);
atomic_fetch_sub(&handler_depth, 1);
return;
}
@ -240,7 +215,6 @@ software_interrupt_disarm_timer(void)
}
}
/**
* Initialize software Interrupts
* Register softint_handler to execute on SIGALRM and SIGUSR1
@ -250,11 +224,12 @@ software_interrupt_initialize(void)
{
struct sigaction signal_action;
memset(&signal_action, 0, sizeof(struct sigaction));
/* All supported signals trigger the same signal handler */
signal_action.sa_sigaction = software_interrupt_handle_signals;
signal_action.sa_flags = SA_SIGINFO | SA_RESTART;
/* all threads created by the calling thread will have signal blocked */
/* TODO: What does sa_mask do? I have to call software_interrupt_mask_signal below */
/* Mask SIGALRM and SIGUSR1 while the signal handler executes */
sigemptyset(&signal_action.sa_mask);
sigaddset(&signal_action.sa_mask, SIGALRM);
sigaddset(&signal_action.sa_mask, SIGUSR1);
@ -264,18 +239,23 @@ software_interrupt_initialize(void)
for (int i = 0; i < supported_signals_len; i++) {
int signal = supported_signals[i];
/* Mask this signal on the listener thread */
software_interrupt_mask_signal(signal);
/* But register the handler for this signal for the process */
if (sigaction(signal, &signal_action, NULL)) {
perror("sigaction");
exit(1);
}
}
software_interrupt_deferred_sigalrm_max_alloc();
software_interrupt_counts_alloc();
}
void
software_interrupt_set_interval_duration(uint64_t cycles)
software_interrupt_cleanup()
{
software_interrupt_interval_duration_in_cycles = cycles;
software_interrupt_counts_deferred_sigalrm_max_update(deferred_sigalrm);
software_interrupt_counts_log();
}

@ -0,0 +1,26 @@
#include "software_interrupt_counts.h"
#include "pretty_print.h"
_Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_max;
_Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_replay;
_Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_kernel;
_Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_thread;
_Atomic volatile sig_atomic_t *software_interrupt_counts_sigusr;
void
software_interrupt_counts_log()
{
#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS
for (int i = 0; i < runtime_worker_threads_count; i++) {
printf("Worker %d:\n", i);
pretty_print_key_value("Deferred Sigalrm Max", "%12d\n",
software_interrupt_counts_deferred_sigalrm_max[i]);
pretty_print_key_value("Deferred Sigalrm Replay", "%12d\n",
software_interrupt_counts_deferred_sigalrm_replay[i]);
pretty_print_key_value("Siglarm Kernel Count", "%12d\n", software_interrupt_counts_sigalrm_kernel[i]);
pretty_print_key_value("Siglarm Thread Count", "%12d\n", software_interrupt_counts_sigalrm_thread[i]);
pretty_print_key_value("Sigusr Count", "%12d\n", software_interrupt_counts_sigusr[i]);
}
fflush(stdout);
#endif
}

@ -66,8 +66,7 @@ worker_thread_main(void *argument)
software_interrupt_unmask_signal(SIGUSR1);
}
/* Idle Loop */
while (true) scheduler_cooperative_sched();
scheduler_idle_loop();
panic("Worker Thread unexpectedly completed run loop.");
panic("Worker Thread unexpectedly completed idle loop.");
}

Loading…
Cancel
Save