├── .gitignore ├── README.md ├── SConstruct ├── book ├── SConscript ├── app │ ├── ex6.md │ ├── ex7.md │ ├── interp.md │ ├── java.md │ ├── libc.md │ ├── proc.md │ └── web.md ├── bootstrap │ ├── LICENSE │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ ├── js │ │ ├── .jshintrc │ │ ├── bootstrap-affix.js │ │ ├── bootstrap-alert.js │ │ ├── bootstrap-button.js │ │ ├── bootstrap-carousel.js │ │ ├── bootstrap-collapse.js │ │ ├── bootstrap-dropdown.js │ │ ├── bootstrap-modal.js │ │ ├── bootstrap-popover.js │ │ ├── bootstrap-scrollspy.js │ │ ├── bootstrap-tab.js │ │ ├── bootstrap-tooltip.js │ │ ├── bootstrap-transition.js │ │ ├── bootstrap-typeahead.js │ │ └── tests │ │ │ ├── index.html │ │ │ ├── phantom.js │ │ │ ├── server.js │ │ │ ├── unit │ │ │ ├── bootstrap-affix.js │ │ │ ├── bootstrap-alert.js │ │ │ ├── bootstrap-button.js │ │ │ ├── bootstrap-carousel.js │ │ │ ├── bootstrap-collapse.js │ │ │ ├── bootstrap-dropdown.js │ │ │ ├── bootstrap-modal.js │ │ │ ├── bootstrap-phantom.js │ │ │ ├── bootstrap-popover.js │ │ │ ├── bootstrap-scrollspy.js │ │ │ ├── bootstrap-tab.js │ │ │ ├── bootstrap-tooltip.js │ │ │ ├── bootstrap-transition.js │ │ │ └── bootstrap-typeahead.js │ │ │ └── vendor │ │ │ ├── jquery.js │ │ │ ├── qunit.css │ │ │ └── qunit.js │ └── less │ │ ├── accordion.less │ │ ├── alerts.less │ │ ├── bootstrap.less │ │ ├── breadcrumbs.less │ │ ├── button-groups.less │ │ ├── buttons.less │ │ ├── carousel.less │ │ ├── close.less │ │ ├── code.less │ │ ├── component-animations.less │ │ ├── dropdowns.less │ │ ├── forms.less │ │ ├── grid.less │ │ ├── hero-unit.less │ │ ├── labels-badges.less │ │ ├── layouts.less │ │ ├── media.less │ │ ├── mixins.less │ │ ├── modals.less │ │ ├── navbar.less │ │ ├── navs.less │ │ ├── pager.less │ │ ├── pagination.less │ │ ├── popovers.less │ │ ├── progress-bars.less │ │ ├── reset.less │ │ ├── responsive-1200px-min.less │ │ ├── responsive-767px-max.less │ │ ├── responsive-768px-979px.less │ │ ├── responsive-navbar.less │ │ ├── responsive-utilities.less │ │ ├── responsive.less │ │ ├── scaffolding.less │ │ ├── sprites.less │ │ ├── tables.less │ │ ├── thumbnails.less │ │ ├── tooltip.less │ │ ├── type.less │ │ ├── utilities.less │ │ ├── variables.less │ │ └── wells.less ├── cheatsheet │ └── cheatsheet.md ├── epub.css ├── hints │ ├── ex1.md │ ├── ex2.md │ ├── ex3.md │ ├── ex4.md │ ├── ex5.md │ ├── ex6.md │ └── ex7.md ├── index.md ├── intro │ ├── conv.md │ ├── experiment.json │ ├── foreword.md │ ├── oskernel.md │ └── tsload.md ├── kernel │ ├── async.md │ ├── bio.md │ ├── ex3.md │ ├── ex4.md │ ├── ex5.md │ ├── fs.md │ ├── irq.md │ ├── net.md │ ├── proc.md │ ├── sched.md │ ├── sobj.md │ └── virtmem.md ├── lab │ ├── iscsi.md │ ├── os.md │ └── web.md ├── lang │ ├── args.md │ ├── assocarr.md │ ├── context.md │ ├── ex1.md │ ├── ex2.md │ ├── intro.md │ ├── pointers.md │ ├── predicates.md │ ├── print.md │ ├── probes.md │ ├── strstr.md │ ├── tapset.md │ ├── time.md │ └── vars.md ├── principles │ ├── apply.md │ ├── dyncode.md │ ├── perf.md │ ├── prepost.md │ ├── profiling.md │ └── viz.md ├── template.html ├── tools │ ├── dtrace.md │ ├── dyntrace.md │ ├── safety.md │ ├── stability.md │ ├── systemtap.md │ └── tracing.md └── watermark.txt ├── experiments ├── concurrency │ └── experiment.json ├── deblock │ └── experiment.json ├── drupal │ └── experiment.json ├── duality │ └── experiment.json ├── pthread │ └── experiment.json └── readahead │ └── experiment.json ├── fonts ├── DejaVuSansMono-Bold.ttf ├── DejaVuSansMono-BoldOblique.ttf ├── DejaVuSansMono-Oblique.ttf ├── DejaVuSansMono.ttf ├── lcmss8.afm └── lcmss8.pfb ├── images ├── SConscript ├── aggrs.svg ├── aio.svg ├── appprobes.svg ├── bio.svg ├── catfiles.svg ├── conv │ ├── array.svg │ ├── embed.svg │ ├── list.svg │ ├── pointer.svg │ └── reverse.svg ├── cpubufs.svg ├── density.png ├── dtrace.svg ├── dyntrace.svg ├── forkproc.svg ├── heatmap.png ├── icons │ ├── book.svg │ ├── dtrace.svg │ ├── manpage.svg │ ├── staplang.svg │ └── stapset.svg ├── java.svg ├── kmem.svg ├── linear.png ├── linux │ ├── bio.svg │ ├── mm.svg │ ├── net.svg │ ├── sched.svg │ ├── task.svg │ └── vfs.svg ├── localglobal.svg ├── merge-svgs.py ├── net.svg ├── netprobes.svg ├── oncpu.png ├── pagetable.svg ├── pas.svg ├── perf.svg ├── pointers.svg ├── prepost.svg ├── probes.svg ├── ringbuf-proto.svg ├── ringbuf.svg ├── sched.svg ├── sobj.svg ├── solaris │ ├── as.svg │ ├── bio.svg │ ├── net.svg │ ├── proc.svg │ ├── sched.svg │ ├── streams.svg │ └── vfs.svg ├── stackfmt.svg ├── stapprocess.svg ├── timeline.svg ├── timer-probe.svg ├── tsload.svg ├── varscope.svg ├── vfsops.svg └── webapp.svg ├── scripts ├── dtrace │ ├── callgraph.d │ ├── cvtrace.d │ ├── deblock.d │ ├── dumptask-lab3.d │ ├── dumptask.d │ ├── forktime.d │ ├── hotspot.d │ ├── kmemstat.d │ ├── mtxtime.d │ ├── openaggr.d │ ├── opentrace.d │ ├── pagefault.d │ ├── pfstat.d │ ├── proc.d │ ├── pthread.d │ ├── pycode.d │ ├── pycode.h │ ├── pymalloc.d │ ├── readahead.d │ ├── sdtrace.d │ ├── stat.d │ ├── topphp.d │ ├── tstrace.d │ ├── web.d │ └── wstat.d ├── src │ ├── hellouser.py │ ├── java │ │ ├── Greeter.java │ │ ├── Greeting.java │ │ └── GreetingThread.java │ ├── jsdt │ │ ├── Greeting.java │ │ ├── GreetingProvider.java │ │ └── JSDT.java │ ├── lab3.c │ ├── openproc.py │ ├── opentrace.py │ └── pthread.c └── stap │ ├── callgraph.stp │ ├── cfstrace.stp │ ├── deblock.stp │ ├── dumptask-lab3.stp │ ├── dumptask.stp │ ├── forktime.stp │ ├── hotspot.stp │ ├── kmemstat.stp │ ├── lstat.stp │ ├── mtxtime.stp │ ├── openaggr.stp │ ├── opentrace.stp │ ├── pagefault.stp │ ├── pfstat.stp │ ├── proc.stp │ ├── pthread.stp │ ├── pycode.stp │ ├── pymalloc.stp │ ├── readahead.stp │ ├── scsitrace.stp │ ├── tapset │ └── lstat.stp │ ├── topphp.stp │ ├── web.stp │ ├── wqtrace.stp │ └── wstat.stp ├── tsdoc ├── gen-cheatsheet.py ├── gen-doc.py └── tsdoc │ ├── __init__.py │ ├── blocks │ ├── __init__.py │ ├── epub.py │ ├── html.py │ ├── latex.py │ ├── markdown.py │ └── pdf.py │ ├── mdparser2.py │ └── page.py └── tsload ├── SConstruct ├── file_opener ├── .ctime_cache ├── SConscript ├── experiment.json ├── file_opener.c ├── include │ └── file_opener.h └── modinfo.cfg └── proc_starter ├── .ctime_cache ├── SConscript ├── experiment.json ├── include ├── proc_starter.h └── shell.h ├── modinfo.json ├── proc_starter.c └── shell.c /.gitignore: -------------------------------------------------------------------------------- 1 | .kdev4 2 | *.kdev4 3 | .sconsign.dblite 4 | build/ 5 | *.pyc 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dtrace-stap-book 2 | 3 | This repository contains source code for book "Dynamic Tracing with DTrace & SystemTap", example scripts and TSLoad modules used in exercises. It is written on extended version of Markdown (which is implemented by TSDoc subsystem) and uses SCons for building. The only supported backend for building book is HTML. 4 | 5 | ### Contents 6 | 7 | This book describes basic principles and languages of dynamic tracing and operating system kernel internals and how they may be exposed with dynamic tracing. Last module of a book is devoted to tracing of userspace applications. 8 | 9 | There are 7 exercises in this book that allow you to try yourself in writing tracing scripts and observe mildly interesting aspects of computer systems. 10 | 11 | ### Errors and requests 12 | 13 | As I am non-native in English, I would appreciate help in editing. If you see ways to expand or improve book, do not hesitate to register an issue so I can consider putting it in further revisions of book. 14 | 15 | ### License 16 | 17 | This work is licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 3.0 License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. 18 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | env = DefaultEnvironment() 5 | 6 | AddOption('--doc-format', dest='doc_format', action="store", default='html', 7 | metavar='FORMAT', help='Documentation format (markdown or html)') 8 | AddOption('--verbose', dest='verbose', action='store_true', default=False, 9 | help='Be verbose') 10 | AddOption('--no-sources', dest='no_sources', action='store_true', default=False, 11 | help='Do not put source files to book') 12 | 13 | LessBuilder = Builder(action = Action('lesscpy $LESSFLAGS $SOURCE > $TARGET'), 14 | suffix = '.css', 15 | src_suffix = '.less') 16 | env.Append(BUILDERS = {'LessBuilder': LessBuilder}) 17 | 18 | VariantDir('build/book/images', 'images') 19 | SConscript('build/book/images/SConscript', 'env') 20 | 21 | VariantDir('build/book', 'book') 22 | SConscript('build/book/SConscript', 'env') -------------------------------------------------------------------------------- /book/SConscript: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from SCons.Errors import StopError 4 | 5 | Import('env') 6 | 7 | def GlobDocs(doc_dir): 8 | return Dir(doc_dir).glob('*.md') 9 | 10 | doc_format = GetOption('doc_format') 11 | if doc_format == 'html': 12 | doc_suffix = '.html' 13 | elif doc_format == 'markdown': 14 | doc_suffix = '.md' 15 | elif doc_format == 'latex': 16 | doc_suffix = '.tex' 17 | elif doc_format == 'pdf': 18 | doc_suffix = '.pdf' 19 | elif doc_format == 'epub': 20 | doc_suffix = '.epub' 21 | else: 22 | raise StopError("Invalid documentation format '%s'" % doc_format) 23 | 24 | DocGenerator = Builder(action = Action('%s tsdoc/gen-doc.py $SOURCES' % (sys.executable)), 25 | src_suffix = '.md', suffix = doc_suffix) 26 | CheatsheetGenerator = Builder(action = Action('%s tsdoc/gen-cheatsheet.py $SOURCE $TARGET' % (sys.executable)), 27 | src_suffix = '.md', suffix = '.pdf') 28 | 29 | env.Append(BUILDERS = {'DocGenerator': DocGenerator, 30 | 'CheatsheetGenerator': CheatsheetGenerator}) 31 | 32 | env.Append(ENV = {'TSDOC_FORMAT': doc_format}) 33 | env.Append(ENV = {'TSDOC_HEADER': 'Dynamic Tracing with DTrace & SystemTap'}) 34 | env.Append(ENV = {'TSDOC_AUTHOR': 'Sergey Klyaus'}) 35 | env.Append(ENV = {'TSDOC_UID': 'dtrace_stap_book'}) 36 | env.Append(ENV = {'TSDOC_IMGDIR': 'build/book/images'}) 37 | env.Append(ENV = {'TSDOC_HTML_TEMPLATE': File('template.html').abspath}) 38 | if GetOption('verbose'): 39 | env.Append(ENV = {'TSDOC_VERBOSE': True}) 40 | if GetOption('no_sources'): 41 | env.Append(ENV = {'TSDOC_NO_SOURCES': True}) 42 | 43 | 44 | index = File('index.md') 45 | 46 | if doc_format == 'html': 47 | cssdir, lessdir = Dir('bootstrap').Dir('css'), Dir('bootstrap').Dir('less') 48 | 49 | for lessname, cssname in [('bootstrap.less', 'bootstrap.css'), 50 | ('responsive.less', 'bootstrap-responsive.css')]: 51 | bsless = lessdir.File(lessname) 52 | bscss = cssdir.File(cssname) 53 | 54 | # Implicit dependencies 55 | for less in lessdir.glob('*.less'): 56 | env.Depends(bscss, less) 57 | 58 | # Build bootstrap from less 59 | env.LessBuilder(bscss, bsless) 60 | env.Depends(index, bscss) 61 | 62 | docs = env.DocGenerator([index] + GlobDocs('intro') + GlobDocs('tools') 63 | + GlobDocs('lang') + GlobDocs('principles') 64 | + GlobDocs('kernel') + GlobDocs('app') 65 | + GlobDocs('hints') + GlobDocs('lab') 66 | + GlobDocs('cheatsheet')) 67 | env.Depends(docs, 'template.html') 68 | env.Depends(docs, 'images') 69 | env.AlwaysBuild(docs) 70 | 71 | csenv = env.Clone() 72 | csenv.Append(ENV = {'TSDOC_HEADER': 'DTrace & SystemTap cheatsheet'}) 73 | cs = csenv.CheatsheetGenerator('cheatsheet.pdf', 'cheatsheet/cheatsheet.md') 74 | -------------------------------------------------------------------------------- /book/app/ex6.md: -------------------------------------------------------------------------------- 1 | ### Exercise 6 2 | 3 | Implement two scripts: `mtxtime.d` and `mtxtimd.stp` that would compute delay between attempt to acquire a userspace mutex and a moment when mutex is acquired. Group times by user stacks and print data as logarithmic histograms. 4 | 5 | Use `pthread` experiment to demonstrate your scripts, like in previous section, TSLoad workload generator itself would be an object in the experiment. Try to identify mutexes that show delays larger than 1 ms. 6 | 7 | !!! WARN 8 | To prevent problems with symbol resolving in DTrace after tracing process finishes, you can attach to a function `experiment_unconfigure()` from `tsexperiment` to print gathered data. 9 | !!! -------------------------------------------------------------------------------- /book/app/ex7.md: -------------------------------------------------------------------------------- 1 | ### Exercise 7 2 | 3 | Create two scripts: `topphp.d` and `topphp.stp` which will measure mean execution time of each PHP function and count number of calls to that function. Group functions by request URI and full function name including class name (if defined). Use `drupal` experiment to demonstrate your script. 4 | 5 | !!! NOTE 6 | It would be reasonable to run workload generator on system other than server (so it won't affect execution of web applications). You can switch roles of virtual machines in lab environment i.e. use Solaris as server and Linux as client and vice versa. To alter server's address, use `-s` option in `tsexperiment` command line: 7 | ``` 8 | # /opt/tsload/bin/tsexperiment -e drupal/ run \ 9 | -s workloads:drupal:params:server=192.168.13.102 10 | ``` 11 | !!! 12 | -------------------------------------------------------------------------------- /book/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/book/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /book/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/book/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /book/bootstrap/js/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "validthis": true, 3 | "laxcomma" : true, 4 | "laxbreak" : true, 5 | "browser" : true, 6 | "eqnull" : true, 7 | "debug" : true, 8 | "devel" : true, 9 | "boss" : true, 10 | "expr" : true, 11 | "asi" : true 12 | } -------------------------------------------------------------------------------- /book/bootstrap/js/bootstrap-alert.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-alert.js v2.3.2 3 | * http://getbootstrap.com/2.3.2/javascript.html#alerts 4 | * ========================================================== 5 | * Copyright 2013 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* ALERT CLASS DEFINITION 27 | * ====================== */ 28 | 29 | var dismiss = '[data-dismiss="alert"]' 30 | , Alert = function (el) { 31 | $(el).on('click', dismiss, this.close) 32 | } 33 | 34 | Alert.prototype.close = function (e) { 35 | var $this = $(this) 36 | , selector = $this.attr('data-target') 37 | , $parent 38 | 39 | if (!selector) { 40 | selector = $this.attr('href') 41 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 42 | } 43 | 44 | $parent = $(selector) 45 | 46 | e && e.preventDefault() 47 | 48 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 49 | 50 | $parent.trigger(e = $.Event('close')) 51 | 52 | if (e.isDefaultPrevented()) return 53 | 54 | $parent.removeClass('in') 55 | 56 | function removeElement() { 57 | $parent 58 | .trigger('closed') 59 | .remove() 60 | } 61 | 62 | $.support.transition && $parent.hasClass('fade') ? 63 | $parent.on($.support.transition.end, removeElement) : 64 | removeElement() 65 | } 66 | 67 | 68 | /* ALERT PLUGIN DEFINITION 69 | * ======================= */ 70 | 71 | var old = $.fn.alert 72 | 73 | $.fn.alert = function (option) { 74 | return this.each(function () { 75 | var $this = $(this) 76 | , data = $this.data('alert') 77 | if (!data) $this.data('alert', (data = new Alert(this))) 78 | if (typeof option == 'string') data[option].call($this) 79 | }) 80 | } 81 | 82 | $.fn.alert.Constructor = Alert 83 | 84 | 85 | /* ALERT NO CONFLICT 86 | * ================= */ 87 | 88 | $.fn.alert.noConflict = function () { 89 | $.fn.alert = old 90 | return this 91 | } 92 | 93 | 94 | /* ALERT DATA-API 95 | * ============== */ 96 | 97 | $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) 98 | 99 | }(window.jQuery); -------------------------------------------------------------------------------- /book/bootstrap/js/bootstrap-button.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-button.js v2.3.2 3 | * http://getbootstrap.com/2.3.2/javascript.html#buttons 4 | * ============================================================ 5 | * Copyright 2013 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* BUTTON PUBLIC CLASS DEFINITION 27 | * ============================== */ 28 | 29 | var Button = function (element, options) { 30 | this.$element = $(element) 31 | this.options = $.extend({}, $.fn.button.defaults, options) 32 | } 33 | 34 | Button.prototype.setState = function (state) { 35 | var d = 'disabled' 36 | , $el = this.$element 37 | , data = $el.data() 38 | , val = $el.is('input') ? 'val' : 'html' 39 | 40 | state = state + 'Text' 41 | data.resetText || $el.data('resetText', $el[val]()) 42 | 43 | $el[val](data[state] || this.options[state]) 44 | 45 | // push to event loop to allow forms to submit 46 | setTimeout(function () { 47 | state == 'loadingText' ? 48 | $el.addClass(d).attr(d, d) : 49 | $el.removeClass(d).removeAttr(d) 50 | }, 0) 51 | } 52 | 53 | Button.prototype.toggle = function () { 54 | var $parent = this.$element.closest('[data-toggle="buttons-radio"]') 55 | 56 | $parent && $parent 57 | .find('.active') 58 | .removeClass('active') 59 | 60 | this.$element.toggleClass('active') 61 | } 62 | 63 | 64 | /* BUTTON PLUGIN DEFINITION 65 | * ======================== */ 66 | 67 | var old = $.fn.button 68 | 69 | $.fn.button = function (option) { 70 | return this.each(function () { 71 | var $this = $(this) 72 | , data = $this.data('button') 73 | , options = typeof option == 'object' && option 74 | if (!data) $this.data('button', (data = new Button(this, options))) 75 | if (option == 'toggle') data.toggle() 76 | else if (option) data.setState(option) 77 | }) 78 | } 79 | 80 | $.fn.button.defaults = { 81 | loadingText: 'loading...' 82 | } 83 | 84 | $.fn.button.Constructor = Button 85 | 86 | 87 | /* BUTTON NO CONFLICT 88 | * ================== */ 89 | 90 | $.fn.button.noConflict = function () { 91 | $.fn.button = old 92 | return this 93 | } 94 | 95 | 96 | /* BUTTON DATA-API 97 | * =============== */ 98 | 99 | $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { 100 | var $btn = $(e.target) 101 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 102 | $btn.button('toggle') 103 | }) 104 | 105 | }(window.jQuery); -------------------------------------------------------------------------------- /book/bootstrap/js/bootstrap-popover.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * bootstrap-popover.js v2.3.2 3 | * http://getbootstrap.com/2.3.2/javascript.html#popovers 4 | * =========================================================== 5 | * Copyright 2013 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * =========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* POPOVER PUBLIC CLASS DEFINITION 27 | * =============================== */ 28 | 29 | var Popover = function (element, options) { 30 | this.init('popover', element, options) 31 | } 32 | 33 | 34 | /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js 35 | ========================================== */ 36 | 37 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { 38 | 39 | constructor: Popover 40 | 41 | , setContent: function () { 42 | var $tip = this.tip() 43 | , title = this.getTitle() 44 | , content = this.getContent() 45 | 46 | $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) 47 | $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content) 48 | 49 | $tip.removeClass('fade top bottom left right in') 50 | } 51 | 52 | , hasContent: function () { 53 | return this.getTitle() || this.getContent() 54 | } 55 | 56 | , getContent: function () { 57 | var content 58 | , $e = this.$element 59 | , o = this.options 60 | 61 | content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) 62 | || $e.attr('data-content') 63 | 64 | return content 65 | } 66 | 67 | , tip: function () { 68 | if (!this.$tip) { 69 | this.$tip = $(this.options.template) 70 | } 71 | return this.$tip 72 | } 73 | 74 | , destroy: function () { 75 | this.hide().$element.off('.' + this.type).removeData(this.type) 76 | } 77 | 78 | }) 79 | 80 | 81 | /* POPOVER PLUGIN DEFINITION 82 | * ======================= */ 83 | 84 | var old = $.fn.popover 85 | 86 | $.fn.popover = function (option) { 87 | return this.each(function () { 88 | var $this = $(this) 89 | , data = $this.data('popover') 90 | , options = typeof option == 'object' && option 91 | if (!data) $this.data('popover', (data = new Popover(this, options))) 92 | if (typeof option == 'string') data[option]() 93 | }) 94 | } 95 | 96 | $.fn.popover.Constructor = Popover 97 | 98 | $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { 99 | placement: 'right' 100 | , trigger: 'click' 101 | , content: '' 102 | , template: '

' 103 | }) 104 | 105 | 106 | /* POPOVER NO CONFLICT 107 | * =================== */ 108 | 109 | $.fn.popover.noConflict = function () { 110 | $.fn.popover = old 111 | return this 112 | } 113 | 114 | }(window.jQuery); 115 | -------------------------------------------------------------------------------- /book/bootstrap/js/bootstrap-transition.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.3.2 3 | * http://getbootstrap.com/2.3.2/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2013 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 27 | * ======================================================= */ 28 | 29 | $(function () { 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd otransitionend' 40 | , 'transition' : 'transitionend' 41 | } 42 | , name 43 | 44 | for (name in transEndEventNames){ 45 | if (el.style[name] !== undefined) { 46 | return transEndEventNames[name] 47 | } 48 | } 49 | 50 | }()) 51 | 52 | return transitionEnd && { 53 | end: transitionEnd 54 | } 55 | 56 | })() 57 | 58 | }) 59 | 60 | }(window.jQuery); -------------------------------------------------------------------------------- /book/bootstrap/js/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap Plugin Test Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |

Bootstrap Plugin Test Suite

50 |

51 |

52 |
    53 |
    54 |
    55 | 56 | -------------------------------------------------------------------------------- /book/bootstrap/js/tests/phantom.js: -------------------------------------------------------------------------------- 1 | // Simple phantom.js integration script 2 | // Adapted from Modernizr 3 | 4 | function waitFor(testFx, onReady, timeOutMillis) { 5 | var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 5001 //< Default Max Timout is 5s 6 | , start = new Date().getTime() 7 | , condition = false 8 | , interval = setInterval(function () { 9 | if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { 10 | // If not time-out yet and condition not yet fulfilled 11 | condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()) //< defensive code 12 | } else { 13 | if (!condition) { 14 | // If condition still not fulfilled (timeout but condition is 'false') 15 | console.log("'waitFor()' timeout") 16 | phantom.exit(1) 17 | } else { 18 | // Condition fulfilled (timeout and/or condition is 'true') 19 | typeof(onReady) === "string" ? eval(onReady) : onReady() //< Do what it's supposed to do once the condition is fulfilled 20 | clearInterval(interval) //< Stop this interval 21 | } 22 | } 23 | }, 100) //< repeat check every 100ms 24 | } 25 | 26 | 27 | if (phantom.args.length === 0 || phantom.args.length > 2) { 28 | console.log('Usage: phantom.js URL') 29 | phantom.exit() 30 | } 31 | 32 | var page = new WebPage() 33 | 34 | // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") 35 | page.onConsoleMessage = function(msg) { 36 | console.log(msg) 37 | }; 38 | 39 | page.open(phantom.args[0], function(status){ 40 | if (status !== "success") { 41 | console.log("Unable to access network") 42 | phantom.exit() 43 | } else { 44 | waitFor(function(){ 45 | return page.evaluate(function(){ 46 | var el = document.getElementById('qunit-testresult') 47 | if (el && el.innerText.match('completed')) { 48 | return true 49 | } 50 | return false 51 | }) 52 | }, function(){ 53 | var failedNum = page.evaluate(function(){ 54 | var el = document.getElementById('qunit-testresult') 55 | try { 56 | return el.getElementsByClassName('failed')[0].innerHTML 57 | } catch (e) { } 58 | return 10000 59 | }); 60 | phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0) 61 | }) 62 | } 63 | }) -------------------------------------------------------------------------------- /book/bootstrap/js/tests/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple connect server for phantom.js 3 | * Adapted from Modernizr 4 | */ 5 | 6 | var connect = require('connect') 7 | , http = require('http') 8 | , fs = require('fs') 9 | , app = connect() 10 | .use(connect.static(__dirname + '/../../')); 11 | 12 | http.createServer(app).listen(3000); 13 | 14 | fs.writeFileSync(__dirname + '/pid.txt', process.pid, 'utf-8') -------------------------------------------------------------------------------- /book/bootstrap/js/tests/unit/bootstrap-affix.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-affix") 4 | 5 | test("should provide no conflict", function () { 6 | var affix = $.fn.affix.noConflict() 7 | ok(!$.fn.affix, 'affix was set back to undefined (org value)') 8 | $.fn.affix = affix 9 | }) 10 | 11 | test("should be defined on jquery object", function () { 12 | ok($(document.body).affix, 'affix method is defined') 13 | }) 14 | 15 | test("should return element", function () { 16 | ok($(document.body).affix()[0] == document.body, 'document.body returned') 17 | }) 18 | 19 | test("should exit early if element is not visible", function () { 20 | var $affix = $('
    ').affix() 21 | $affix.data('affix').checkPosition() 22 | ok(!$affix.hasClass('affix'), 'affix class was not added') 23 | }) 24 | 25 | }) -------------------------------------------------------------------------------- /book/bootstrap/js/tests/unit/bootstrap-alert.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-alerts") 4 | 5 | test("should provide no conflict", function () { 6 | var alert = $.fn.alert.noConflict() 7 | ok(!$.fn.alert, 'alert was set back to undefined (org value)') 8 | $.fn.alert = alert 9 | }) 10 | 11 | test("should be defined on jquery object", function () { 12 | ok($(document.body).alert, 'alert method is defined') 13 | }) 14 | 15 | test("should return element", function () { 16 | ok($(document.body).alert()[0] == document.body, 'document.body returned') 17 | }) 18 | 19 | test("should fade element out on clicking .close", function () { 20 | var alertHTML = '
    ' 21 | + '×' 22 | + '

    Holy guacamole! Best check yo self, you\'re not looking too good.

    ' 23 | + '
    ' 24 | , alert = $(alertHTML).alert() 25 | 26 | alert.find('.close').click() 27 | 28 | ok(!alert.hasClass('in'), 'remove .in class on .close click') 29 | }) 30 | 31 | test("should remove element when clicking .close", function () { 32 | $.support.transition = false 33 | 34 | var alertHTML = '
    ' 35 | + '×' 36 | + '

    Holy guacamole! Best check yo self, you\'re not looking too good.

    ' 37 | + '
    ' 38 | , alert = $(alertHTML).appendTo('#qunit-fixture').alert() 39 | 40 | ok($('#qunit-fixture').find('.alert-message').length, 'element added to dom') 41 | 42 | alert.find('.close').click() 43 | 44 | ok(!$('#qunit-fixture').find('.alert-message').length, 'element removed from dom') 45 | }) 46 | 47 | test("should not fire closed when close is prevented", function () { 48 | $.support.transition = false 49 | stop(); 50 | $('
    ') 51 | .bind('close', function (e) { 52 | e.preventDefault(); 53 | ok(true); 54 | start(); 55 | }) 56 | .bind('closed', function () { 57 | ok(false); 58 | }) 59 | .alert('close') 60 | }) 61 | 62 | }) -------------------------------------------------------------------------------- /book/bootstrap/js/tests/unit/bootstrap-collapse.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-collapse") 4 | 5 | test("should provide no conflict", function () { 6 | var collapse = $.fn.collapse.noConflict() 7 | ok(!$.fn.collapse, 'collapse was set back to undefined (org value)') 8 | $.fn.collapse = collapse 9 | }) 10 | 11 | test("should be defined on jquery object", function () { 12 | ok($(document.body).collapse, 'collapse method is defined') 13 | }) 14 | 15 | test("should return element", function () { 16 | ok($(document.body).collapse()[0] == document.body, 'document.body returned') 17 | }) 18 | 19 | test("should show a collapsed element", function () { 20 | var el = $('
    ').collapse('show') 21 | ok(el.hasClass('in'), 'has class in') 22 | ok(/height/.test(el.attr('style')), 'has height set') 23 | }) 24 | 25 | test("should hide a collapsed element", function () { 26 | var el = $('
    ').collapse('hide') 27 | ok(!el.hasClass('in'), 'does not have class in') 28 | ok(/height/.test(el.attr('style')), 'has height set') 29 | }) 30 | 31 | test("should not fire shown when show is prevented", function () { 32 | $.support.transition = false 33 | stop() 34 | $('
    ') 35 | .bind('show', function (e) { 36 | e.preventDefault(); 37 | ok(true); 38 | start(); 39 | }) 40 | .bind('shown', function () { 41 | ok(false); 42 | }) 43 | .collapse('show') 44 | }) 45 | 46 | test("should reset style to auto after finishing opening collapse", function () { 47 | $.support.transition = false 48 | stop() 49 | $('
    ') 50 | .bind('show', function () { 51 | ok(this.style.height == '0px') 52 | }) 53 | .bind('shown', function () { 54 | ok(this.style.height == 'auto') 55 | start() 56 | }) 57 | .collapse('show') 58 | }) 59 | 60 | test("should add active class to target when collapse shown", function () { 61 | $.support.transition = false 62 | stop() 63 | 64 | var target = $('') 65 | .appendTo($('#qunit-fixture')) 66 | 67 | var collapsible = $('
    ') 68 | .appendTo($('#qunit-fixture')) 69 | .on('show', function () { 70 | ok(!target.hasClass('collapsed')) 71 | start() 72 | }) 73 | 74 | target.click() 75 | }) 76 | 77 | test("should remove active class to target when collapse hidden", function () { 78 | $.support.transition = false 79 | stop() 80 | 81 | var target = $('') 82 | .appendTo($('#qunit-fixture')) 83 | 84 | var collapsible = $('
    ') 85 | .appendTo($('#qunit-fixture')) 86 | .on('hide', function () { 87 | ok(target.hasClass('collapsed')) 88 | start() 89 | }) 90 | 91 | target.click() 92 | }) 93 | 94 | }) -------------------------------------------------------------------------------- /book/bootstrap/js/tests/unit/bootstrap-phantom.js: -------------------------------------------------------------------------------- 1 | // Logging setup for phantom integration 2 | // adapted from Modernizr 3 | 4 | QUnit.begin = function () { 5 | console.log("Starting test suite") 6 | console.log("================================================\n") 7 | } 8 | 9 | QUnit.moduleDone = function (opts) { 10 | if (opts.failed === 0) { 11 | console.log("\u2714 All tests passed in '" + opts.name + "' module") 12 | } else { 13 | console.log("\u2716 " + opts.failed + " tests failed in '" + opts.name + "' module") 14 | } 15 | } 16 | 17 | QUnit.done = function (opts) { 18 | console.log("\n================================================") 19 | console.log("Tests completed in " + opts.runtime + " milliseconds") 20 | console.log(opts.passed + " tests of " + opts.total + " passed, " + opts.failed + " failed.") 21 | } -------------------------------------------------------------------------------- /book/bootstrap/js/tests/unit/bootstrap-scrollspy.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-scrollspy") 4 | 5 | test("should provide no conflict", function () { 6 | var scrollspy = $.fn.scrollspy.noConflict() 7 | ok(!$.fn.scrollspy, 'scrollspy was set back to undefined (org value)') 8 | $.fn.scrollspy = scrollspy 9 | }) 10 | 11 | test("should be defined on jquery object", function () { 12 | ok($(document.body).scrollspy, 'scrollspy method is defined') 13 | }) 14 | 15 | test("should return element", function () { 16 | ok($(document.body).scrollspy()[0] == document.body, 'document.body returned') 17 | }) 18 | 19 | test("should switch active class on scroll", function () { 20 | var sectionHTML = '
    ' 21 | , $section = $(sectionHTML).append('#qunit-fixture') 22 | , topbarHTML ='
    ' 23 | + '
    ' 24 | + '
    ' 25 | + '

    Bootstrap

    ' 26 | + '' 29 | + '
    ' 30 | + '
    ' 31 | + '
    ' 32 | , $topbar = $(topbarHTML).scrollspy() 33 | 34 | ok($topbar.find('.active', true)) 35 | }) 36 | 37 | }) -------------------------------------------------------------------------------- /book/bootstrap/js/tests/unit/bootstrap-tab.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-tabs") 4 | 5 | test("should provide no conflict", function () { 6 | var tab = $.fn.tab.noConflict() 7 | ok(!$.fn.tab, 'tab was set back to undefined (org value)') 8 | $.fn.tab = tab 9 | }) 10 | 11 | test("should be defined on jquery object", function () { 12 | ok($(document.body).tab, 'tabs method is defined') 13 | }) 14 | 15 | test("should return element", function () { 16 | ok($(document.body).tab()[0] == document.body, 'document.body returned') 17 | }) 18 | 19 | test("should activate element by tab id", function () { 20 | var tabsHTML = 21 | '' 25 | 26 | $('
    ').appendTo("#qunit-fixture") 27 | 28 | $(tabsHTML).find('li:last a').tab('show') 29 | equals($("#qunit-fixture").find('.active').attr('id'), "profile") 30 | 31 | $(tabsHTML).find('li:first a').tab('show') 32 | equals($("#qunit-fixture").find('.active').attr('id'), "home") 33 | }) 34 | 35 | test("should activate element by tab id", function () { 36 | var pillsHTML = 37 | '' 41 | 42 | $('
    ').appendTo("#qunit-fixture") 43 | 44 | $(pillsHTML).find('li:last a').tab('show') 45 | equals($("#qunit-fixture").find('.active').attr('id'), "profile") 46 | 47 | $(pillsHTML).find('li:first a').tab('show') 48 | equals($("#qunit-fixture").find('.active').attr('id'), "home") 49 | }) 50 | 51 | 52 | test("should not fire closed when close is prevented", function () { 53 | $.support.transition = false 54 | stop(); 55 | $('
    ') 56 | .bind('show', function (e) { 57 | e.preventDefault(); 58 | ok(true); 59 | start(); 60 | }) 61 | .bind('shown', function () { 62 | ok(false); 63 | }) 64 | .tab('show') 65 | }) 66 | 67 | test("show and shown events should reference correct relatedTarget", function () { 68 | var dropHTML = 69 | '
      ' 70 | + '' 76 | + '
    ' 77 | 78 | $(dropHTML).find('ul>li:first a').tab('show').end() 79 | .find('ul>li:last a').on('show', function(event){ 80 | equals(event.relatedTarget.hash, "#1-1") 81 | }).on('shown', function(event){ 82 | equals(event.relatedTarget.hash, "#1-1") 83 | }).tab('show') 84 | }) 85 | 86 | }) -------------------------------------------------------------------------------- /book/bootstrap/js/tests/unit/bootstrap-transition.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-transition") 4 | 5 | test("should be defined on jquery support object", function () { 6 | ok($.support.transition !== undefined, 'transition object is defined') 7 | }) 8 | 9 | test("should provide an end object", function () { 10 | ok($.support.transition ? $.support.transition.end : true, 'end string is defined') 11 | }) 12 | 13 | }) -------------------------------------------------------------------------------- /book/bootstrap/less/accordion.less: -------------------------------------------------------------------------------- 1 | // 2 | // Accordion 3 | // -------------------------------------------------- 4 | 5 | 6 | // Parent container 7 | .accordion { 8 | margin-bottom: @baseLineHeight; 9 | } 10 | 11 | // Group == heading + body 12 | .accordion-group { 13 | margin-bottom: 2px; 14 | border: 1px solid #e5e5e5; 15 | .border-radius(@baseBorderRadius); 16 | } 17 | .accordion-heading { 18 | border-bottom: 0; 19 | } 20 | .accordion-heading .accordion-toggle { 21 | display: block; 22 | padding: 8px 15px; 23 | } 24 | 25 | // General toggle styles 26 | .accordion-toggle { 27 | cursor: pointer; 28 | } 29 | 30 | // Inner needs the styles because you can't animate properly with any styles on the element 31 | .accordion-inner { 32 | padding: 9px 15px; 33 | border-top: 1px solid #e5e5e5; 34 | } 35 | -------------------------------------------------------------------------------- /book/bootstrap/less/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: 8px 35px 8px 14px; 11 | margin-bottom: @baseLineHeight; 12 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 13 | background-color: @warningBackground; 14 | border: 1px solid @warningBorder; 15 | .border-radius(@baseBorderRadius); 16 | } 17 | .alert, 18 | .alert h4 { 19 | // Specified for the h4 to prevent conflicts of changing @headingsColor 20 | color: @warningText; 21 | } 22 | .alert h4 { 23 | margin: 0; 24 | } 25 | 26 | // Adjust close link position 27 | .alert .close { 28 | position: relative; 29 | top: -2px; 30 | right: -21px; 31 | line-height: @baseLineHeight; 32 | } 33 | 34 | 35 | // Alternate styles 36 | // ------------------------- 37 | 38 | .alert-success { 39 | background-color: @successBackground; 40 | border-color: @successBorder; 41 | color: @successText; 42 | } 43 | .alert-success h4 { 44 | color: @successText; 45 | } 46 | .alert-danger, 47 | .alert-error { 48 | background-color: @errorBackground; 49 | border-color: @errorBorder; 50 | color: @errorText; 51 | } 52 | .alert-danger h4, 53 | .alert-error h4 { 54 | color: @errorText; 55 | } 56 | .alert-info { 57 | background-color: @infoBackground; 58 | border-color: @infoBorder; 59 | color: @infoText; 60 | } 61 | .alert-info h4 { 62 | color: @infoText; 63 | } 64 | 65 | 66 | // Block alerts 67 | // ------------------------- 68 | 69 | .alert-block { 70 | padding-top: 14px; 71 | padding-bottom: 14px; 72 | } 73 | .alert-block > p, 74 | .alert-block > ul { 75 | margin-bottom: 0; 76 | } 77 | .alert-block p + p { 78 | margin-top: 5px; 79 | } 80 | -------------------------------------------------------------------------------- /book/bootstrap/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.3.2 3 | * 4 | * Copyright 2013 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world by @mdo and @fat. 9 | */ 10 | 11 | // Core variables and mixins 12 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 13 | @import "mixins.less"; 14 | 15 | // CSS Reset 16 | @import "reset.less"; 17 | 18 | // Grid system and page structure 19 | @import "scaffolding.less"; 20 | @import "grid.less"; 21 | @import "layouts.less"; 22 | 23 | // Base CSS 24 | @import "type.less"; 25 | @import "code.less"; 26 | // @import "forms.less"; 27 | @import "tables.less"; 28 | 29 | // Components: common 30 | // @import "sprites.less"; 31 | // @import "dropdowns.less"; 32 | @import "wells.less"; 33 | @import "component-animations.less"; 34 | @import "close.less"; 35 | 36 | // Components: Buttons & Alerts 37 | @import "buttons.less"; 38 | // @import "button-groups.less"; 39 | @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less 40 | 41 | // Components: Nav 42 | @import "navs.less"; 43 | @import "navbar.less"; 44 | @import "breadcrumbs.less"; 45 | // @import "pagination.less"; 46 | // @import "pager.less"; 47 | 48 | // Components: Popovers 49 | @import "modals.less"; 50 | @import "tooltip.less"; 51 | @import "popovers.less"; 52 | 53 | // Components: Misc 54 | @import "thumbnails.less"; 55 | @import "media.less"; 56 | @import "labels-badges.less"; 57 | // @import "progress-bars.less"; 58 | // @import "accordion.less"; 59 | // @import "carousel.less"; 60 | // @import "hero-unit.less"; 61 | 62 | // Utility classes 63 | @import "utilities.less"; // Has to be last to override when necessary 64 | -------------------------------------------------------------------------------- /book/bootstrap/less/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: 8px 15px; 8 | margin: 0 0 @baseLineHeight; 9 | list-style: none; 10 | background-color: #f5f5f5; 11 | .border-radius(@baseBorderRadius); 12 | > li { 13 | display: inline-block; 14 | .ie7-inline-block(); 15 | text-shadow: 0 1px 0 @white; 16 | > .divider { 17 | padding: 0 5px; 18 | color: #ccc; 19 | } 20 | } 21 | > .active { 22 | color: @grayLight; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /book/bootstrap/less/carousel.less: -------------------------------------------------------------------------------- 1 | // 2 | // Carousel 3 | // -------------------------------------------------- 4 | 5 | 6 | .carousel { 7 | position: relative; 8 | margin-bottom: @baseLineHeight; 9 | line-height: 1; 10 | } 11 | 12 | .carousel-inner { 13 | overflow: hidden; 14 | width: 100%; 15 | position: relative; 16 | } 17 | 18 | .carousel-inner { 19 | 20 | > .item { 21 | display: none; 22 | position: relative; 23 | .transition(.6s ease-in-out left); 24 | 25 | // Account for jankitude on images 26 | > img, 27 | > a > img { 28 | display: block; 29 | line-height: 1; 30 | } 31 | } 32 | 33 | > .active, 34 | > .next, 35 | > .prev { display: block; } 36 | 37 | > .active { 38 | left: 0; 39 | } 40 | 41 | > .next, 42 | > .prev { 43 | position: absolute; 44 | top: 0; 45 | width: 100%; 46 | } 47 | 48 | > .next { 49 | left: 100%; 50 | } 51 | > .prev { 52 | left: -100%; 53 | } 54 | > .next.left, 55 | > .prev.right { 56 | left: 0; 57 | } 58 | 59 | > .active.left { 60 | left: -100%; 61 | } 62 | > .active.right { 63 | left: 100%; 64 | } 65 | 66 | } 67 | 68 | // Left/right controls for nav 69 | // --------------------------- 70 | 71 | .carousel-control { 72 | position: absolute; 73 | top: 40%; 74 | left: 15px; 75 | width: 40px; 76 | height: 40px; 77 | margin-top: -20px; 78 | font-size: 60px; 79 | font-weight: 100; 80 | line-height: 30px; 81 | color: @white; 82 | text-align: center; 83 | background: @grayDarker; 84 | border: 3px solid @white; 85 | .border-radius(23px); 86 | .opacity(50); 87 | 88 | // we can't have this transition here 89 | // because webkit cancels the carousel 90 | // animation if you trip this while 91 | // in the middle of another animation 92 | // ;_; 93 | // .transition(opacity .2s linear); 94 | 95 | // Reposition the right one 96 | &.right { 97 | left: auto; 98 | right: 15px; 99 | } 100 | 101 | // Hover/focus state 102 | &:hover, 103 | &:focus { 104 | color: @white; 105 | text-decoration: none; 106 | .opacity(90); 107 | } 108 | } 109 | 110 | // Carousel indicator pips 111 | // ----------------------------- 112 | .carousel-indicators { 113 | position: absolute; 114 | top: 15px; 115 | right: 15px; 116 | z-index: 5; 117 | margin: 0; 118 | list-style: none; 119 | 120 | li { 121 | display: block; 122 | float: left; 123 | width: 10px; 124 | height: 10px; 125 | margin-left: 5px; 126 | text-indent: -999px; 127 | background-color: #ccc; 128 | background-color: rgba(255,255,255,.25); 129 | border-radius: 5px; 130 | } 131 | .active { 132 | background-color: #fff; 133 | } 134 | } 135 | 136 | // Caption for text below images 137 | // ----------------------------- 138 | 139 | .carousel-caption { 140 | position: absolute; 141 | left: 0; 142 | right: 0; 143 | bottom: 0; 144 | padding: 15px; 145 | background: @grayDark; 146 | background: rgba(0,0,0,.75); 147 | } 148 | .carousel-caption h4, 149 | .carousel-caption p { 150 | color: @white; 151 | line-height: @baseLineHeight; 152 | } 153 | .carousel-caption h4 { 154 | margin: 0 0 5px; 155 | } 156 | .carousel-caption p { 157 | margin-bottom: 0; 158 | } 159 | -------------------------------------------------------------------------------- /book/bootstrap/less/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: 20px; 9 | font-weight: bold; 10 | line-height: @baseLineHeight; 11 | color: @black; 12 | text-shadow: 0 1px 0 rgba(255,255,255,1); 13 | .opacity(20); 14 | &:hover, 15 | &:focus { 16 | color: @black; 17 | text-decoration: none; 18 | cursor: pointer; 19 | .opacity(40); 20 | } 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | button.close { 27 | padding: 0; 28 | cursor: pointer; 29 | background: transparent; 30 | border: 0; 31 | -webkit-appearance: none; 32 | } -------------------------------------------------------------------------------- /book/bootstrap/less/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and blocK) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | pre { 9 | padding: 0 3px 2px; 10 | #font > #family > .monospace; 11 | font-size: @baseFontSize - 2; 12 | color: @grayDark; 13 | // .border-radius(3px); 14 | } 15 | 16 | // Inline code 17 | code { 18 | padding: 2px 4px; 19 | color: #0B0B61; 20 | background-color: #EFEFFB; 21 | white-space: nowrap; 22 | } 23 | 24 | // Small code 25 | small code { 26 | font-size: @baseFontSize - 4; 27 | } 28 | 29 | // Blocks of code 30 | pre { 31 | display: block; 32 | padding: (@baseLineHeight - 1) / 2; 33 | margin: 0 0 @baseLineHeight / 2; 34 | font-size: @baseFontSize - 1; // 14px to 13px 35 | line-height: @baseLineHeight; 36 | // word-break: break-all; 37 | // word-wrap: break-word; 38 | // white-space: pre; 39 | // white-space: pre-wrap; 40 | background-color: #EFFBFB; 41 | border: 1px solid #ccc; // fallback for IE7-8 42 | border: 1px solid rgba(0,0,0,.15); 43 | border-right: 3px solid #8181F7; 44 | border-bottom: 3px solid #8181F7; 45 | 46 | // Make prettyprint styles more spaced out for readability 47 | &.prettyprint { 48 | margin-bottom: @baseLineHeight; 49 | } 50 | 51 | // Account for some code outputs that place code tags in pre tags 52 | code { 53 | padding: 0; 54 | color: inherit; 55 | white-space: pre; 56 | white-space: pre-wrap; 57 | background-color: transparent; 58 | border: 0; 59 | } 60 | } 61 | 62 | // Enable scrollable blocks of code 63 | .pre-scrollable { 64 | max-height: 340px; 65 | overflow-y: scroll; 66 | } 67 | 68 | // Make base font size lesser in scripts 69 | div.well pre { 70 | font-size: @baseFontSize - 4; 71 | } -------------------------------------------------------------------------------- /book/bootstrap/less/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | 6 | .fade { 7 | opacity: 0; 8 | .transition(opacity .15s linear); 9 | &.in { 10 | opacity: 1; 11 | } 12 | } 13 | 14 | .collapse { 15 | position: relative; 16 | height: 0; 17 | overflow: hidden; 18 | .transition(height .35s ease); 19 | &.in { 20 | height: auto; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /book/bootstrap/less/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Fixed (940px) 7 | #grid > .core(@gridColumnWidth, @gridGutterWidth); 8 | 9 | // Fluid (940px) 10 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); 11 | 12 | // Reset utility classes due to specificity 13 | [class*="span"].hide, 14 | .row-fluid [class*="span"].hide { 15 | display: none; 16 | } 17 | 18 | [class*="span"].pull-right, 19 | .row-fluid [class*="span"].pull-right { 20 | float: right; 21 | } 22 | -------------------------------------------------------------------------------- /book/bootstrap/less/hero-unit.less: -------------------------------------------------------------------------------- 1 | // 2 | // Hero unit 3 | // -------------------------------------------------- 4 | 5 | 6 | .hero-unit { 7 | padding: 60px; 8 | margin-bottom: 30px; 9 | font-size: 18px; 10 | font-weight: 200; 11 | line-height: @baseLineHeight * 1.5; 12 | color: @heroUnitLeadColor; 13 | background-color: @heroUnitBackground; 14 | .border-radius(6px); 15 | h1 { 16 | margin-bottom: 0; 17 | font-size: 60px; 18 | line-height: 1; 19 | color: @heroUnitHeadingColor; 20 | letter-spacing: -1px; 21 | } 22 | li { 23 | line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /book/bootstrap/less/labels-badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels and badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .label, 8 | .badge { 9 | display: inline-block; 10 | padding: 2px 4px; 11 | font-size: @baseFontSize * .846; 12 | font-weight: bold; 13 | line-height: 14px; // ensure proper line-height if floated 14 | color: @white; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 18 | background-color: @grayLight; 19 | } 20 | // Set unique padding and border-radii 21 | .label { 22 | .border-radius(3px); 23 | } 24 | .badge { 25 | padding-left: 9px; 26 | padding-right: 9px; 27 | .border-radius(9px); 28 | } 29 | 30 | // Empty labels/badges collapse 31 | .label, 32 | .badge { 33 | &:empty { 34 | display: none; 35 | } 36 | } 37 | 38 | // Hover/focus state, but only for links 39 | a { 40 | &.label:hover, 41 | &.label:focus, 42 | &.badge:hover, 43 | &.badge:focus { 44 | color: @white; 45 | text-decoration: none; 46 | cursor: pointer; 47 | } 48 | } 49 | 50 | // Colors 51 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) 52 | .label, 53 | .badge { 54 | // Important (red) 55 | &-important { background-color: @errorText; } 56 | &-important[href] { background-color: darken(@errorText, 10%); } 57 | // Warnings (orange) 58 | &-warning { background-color: @orange; } 59 | &-warning[href] { background-color: darken(@orange, 10%); } 60 | // Success (green) 61 | &-success { background-color: @successText; } 62 | &-success[href] { background-color: darken(@successText, 10%); } 63 | // Info (turquoise) 64 | &-info { background-color: @infoText; } 65 | &-info[href] { background-color: darken(@infoText, 10%); } 66 | // Inverse (black) 67 | &-inverse { background-color: @grayDark; } 68 | &-inverse[href] { background-color: darken(@grayDark, 10%); } 69 | } 70 | 71 | // Quick fix for labels/badges in buttons 72 | .btn { 73 | .label, 74 | .badge { 75 | position: relative; 76 | top: -1px; 77 | } 78 | } 79 | .btn-mini { 80 | .label, 81 | .badge { 82 | top: 0; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /book/bootstrap/less/layouts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Layouts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container (centered, fixed-width layouts) 7 | .container { 8 | .container-fixed(); 9 | } 10 | 11 | // Fluid layouts (left aligned, with sidebar, min- & max-width content) 12 | .container-fluid { 13 | padding-right: @gridGutterWidth; 14 | padding-left: @gridGutterWidth; 15 | .clearfix(); 16 | } -------------------------------------------------------------------------------- /book/bootstrap/less/media.less: -------------------------------------------------------------------------------- 1 | // Media objects 2 | // Source: http://stubbornella.org/content/?p=497 3 | // -------------------------------------------------- 4 | 5 | 6 | // Common styles 7 | // ------------------------- 8 | 9 | // Clear the floats 10 | .media, 11 | .media-body { 12 | overflow: hidden; 13 | *overflow: visible; 14 | zoom: 1; 15 | } 16 | 17 | // Proper spacing between instances of .media 18 | .media, 19 | .media .media { 20 | margin-top: 15px; 21 | } 22 | .media:first-child { 23 | margin-top: 0; 24 | } 25 | 26 | // For images and videos, set to block 27 | .media-object { 28 | display: block; 29 | } 30 | 31 | // Reset margins on headings for tighter default spacing 32 | .media-heading { 33 | margin: 0 0 5px; 34 | } 35 | 36 | 37 | // Media image alignment 38 | // ------------------------- 39 | 40 | .media > .pull-left { 41 | margin-right: 10px; 42 | } 43 | .media > .pull-right { 44 | margin-left: 10px; 45 | } 46 | 47 | 48 | // Media list variation 49 | // ------------------------- 50 | 51 | // Undo default ul/ol styles 52 | .media-list { 53 | margin-left: 0; 54 | list-style: none; 55 | } 56 | -------------------------------------------------------------------------------- /book/bootstrap/less/modals.less: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // -------------------------------------------------- 4 | 5 | // Background 6 | .modal-backdrop { 7 | position: fixed; 8 | top: 0; 9 | right: 0; 10 | bottom: 0; 11 | left: 0; 12 | z-index: @zindexModalBackdrop; 13 | background-color: @black; 14 | // Fade for backdrop 15 | &.fade { opacity: 0; } 16 | } 17 | 18 | .modal-backdrop, 19 | .modal-backdrop.fade.in { 20 | .opacity(80); 21 | } 22 | 23 | // Base modal 24 | .modal { 25 | position: fixed; 26 | top: 10%; 27 | left: 50%; 28 | z-index: @zindexModal; 29 | width: 560px; 30 | margin-left: -280px; 31 | background-color: @white; 32 | border: 1px solid #999; 33 | border: 1px solid rgba(0,0,0,.3); 34 | *border: 1px solid #999; /* IE6-7 */ 35 | .border-radius(6px); 36 | .box-shadow(0 3px 7px rgba(0,0,0,0.3)); 37 | .background-clip(padding-box); 38 | // Remove focus outline from opened modal 39 | outline: none; 40 | 41 | &.fade { 42 | .transition(e('opacity .3s linear, top .3s ease-out')); 43 | top: -25%; 44 | } 45 | &.fade.in { top: 10%; } 46 | } 47 | .modal-header { 48 | padding: 9px 15px; 49 | border-bottom: 1px solid #eee; 50 | // Close icon 51 | .close { margin-top: 2px; } 52 | // Heading 53 | h3 { 54 | margin: 0; 55 | line-height: 30px; 56 | } 57 | } 58 | 59 | // Body (where all modal content resides) 60 | .modal-body { 61 | position: relative; 62 | overflow-y: auto; 63 | max-height: 400px; 64 | padding: 15px; 65 | } 66 | // Remove bottom margin if need be 67 | .modal-form { 68 | margin-bottom: 0; 69 | } 70 | 71 | // Footer (for actions) 72 | .modal-footer { 73 | padding: 14px 15px 15px; 74 | margin-bottom: 0; 75 | text-align: right; // right align buttons 76 | background-color: #f5f5f5; 77 | border-top: 1px solid #ddd; 78 | .border-radius(0 0 6px 6px); 79 | .box-shadow(inset 0 1px 0 @white); 80 | .clearfix(); // clear it in case folks use .pull-* classes on buttons 81 | 82 | // Properly space out buttons 83 | .btn + .btn { 84 | margin-left: 5px; 85 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 86 | } 87 | // but override that for button groups 88 | .btn-group .btn + .btn { 89 | margin-left: -1px; 90 | } 91 | // and override it for block buttons as well 92 | .btn-block + .btn-block { 93 | margin-left: 0; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /book/bootstrap/less/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | margin: @baseLineHeight 0; 8 | list-style: none; 9 | text-align: center; 10 | .clearfix(); 11 | } 12 | .pager li { 13 | display: inline; 14 | } 15 | .pager li > a, 16 | .pager li > span { 17 | display: inline-block; 18 | padding: 5px 14px; 19 | background-color: #fff; 20 | border: 1px solid #ddd; 21 | .border-radius(15px); 22 | } 23 | .pager li > a:hover, 24 | .pager li > a:focus { 25 | text-decoration: none; 26 | background-color: #f5f5f5; 27 | } 28 | .pager .next > a, 29 | .pager .next > span { 30 | float: right; 31 | } 32 | .pager .previous > a, 33 | .pager .previous > span { 34 | float: left; 35 | } 36 | .pager .disabled > a, 37 | .pager .disabled > a:hover, 38 | .pager .disabled > a:focus, 39 | .pager .disabled > span { 40 | color: @grayLight; 41 | background-color: #fff; 42 | cursor: default; 43 | } -------------------------------------------------------------------------------- /book/bootstrap/less/pagination.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination (multiple pages) 3 | // -------------------------------------------------- 4 | 5 | // Space out pagination from surrounding content 6 | .pagination { 7 | margin: @baseLineHeight 0; 8 | } 9 | 10 | .pagination ul { 11 | // Allow for text-based alignment 12 | display: inline-block; 13 | .ie7-inline-block(); 14 | // Reset default ul styles 15 | margin-left: 0; 16 | margin-bottom: 0; 17 | // Visuals 18 | .border-radius(@baseBorderRadius); 19 | .box-shadow(0 1px 2px rgba(0,0,0,.05)); 20 | } 21 | .pagination ul > li { 22 | display: inline; // Remove list-style and block-level defaults 23 | } 24 | .pagination ul > li > a, 25 | .pagination ul > li > span { 26 | float: left; // Collapse white-space 27 | padding: 4px 12px; 28 | line-height: @baseLineHeight; 29 | text-decoration: none; 30 | background-color: @paginationBackground; 31 | border: 1px solid @paginationBorder; 32 | border-left-width: 0; 33 | } 34 | .pagination ul > li > a:hover, 35 | .pagination ul > li > a:focus, 36 | .pagination ul > .active > a, 37 | .pagination ul > .active > span { 38 | background-color: @paginationActiveBackground; 39 | } 40 | .pagination ul > .active > a, 41 | .pagination ul > .active > span { 42 | color: @grayLight; 43 | cursor: default; 44 | } 45 | .pagination ul > .disabled > span, 46 | .pagination ul > .disabled > a, 47 | .pagination ul > .disabled > a:hover, 48 | .pagination ul > .disabled > a:focus { 49 | color: @grayLight; 50 | background-color: transparent; 51 | cursor: default; 52 | } 53 | .pagination ul > li:first-child > a, 54 | .pagination ul > li:first-child > span { 55 | border-left-width: 1px; 56 | .border-left-radius(@baseBorderRadius); 57 | } 58 | .pagination ul > li:last-child > a, 59 | .pagination ul > li:last-child > span { 60 | .border-right-radius(@baseBorderRadius); 61 | } 62 | 63 | 64 | // Alignment 65 | // -------------------------------------------------- 66 | 67 | .pagination-centered { 68 | text-align: center; 69 | } 70 | .pagination-right { 71 | text-align: right; 72 | } 73 | 74 | 75 | // Sizing 76 | // -------------------------------------------------- 77 | 78 | // Large 79 | .pagination-large { 80 | ul > li > a, 81 | ul > li > span { 82 | padding: @paddingLarge; 83 | font-size: @fontSizeLarge; 84 | } 85 | ul > li:first-child > a, 86 | ul > li:first-child > span { 87 | .border-left-radius(@borderRadiusLarge); 88 | } 89 | ul > li:last-child > a, 90 | ul > li:last-child > span { 91 | .border-right-radius(@borderRadiusLarge); 92 | } 93 | } 94 | 95 | // Small and mini 96 | .pagination-mini, 97 | .pagination-small { 98 | ul > li:first-child > a, 99 | ul > li:first-child > span { 100 | .border-left-radius(@borderRadiusSmall); 101 | } 102 | ul > li:last-child > a, 103 | ul > li:last-child > span { 104 | .border-right-radius(@borderRadiusSmall); 105 | } 106 | } 107 | 108 | // Small 109 | .pagination-small { 110 | ul > li > a, 111 | ul > li > span { 112 | padding: @paddingSmall; 113 | font-size: @fontSizeSmall; 114 | } 115 | } 116 | // Mini 117 | .pagination-mini { 118 | ul > li > a, 119 | ul > li > span { 120 | padding: @paddingMini; 121 | font-size: @fontSizeMini; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /book/bootstrap/less/progress-bars.less: -------------------------------------------------------------------------------- 1 | // 2 | // Progress bars 3 | // -------------------------------------------------- 4 | 5 | 6 | // ANIMATIONS 7 | // ---------- 8 | 9 | // Webkit 10 | @-webkit-keyframes progress-bar-stripes { 11 | from { background-position: 40px 0; } 12 | to { background-position: 0 0; } 13 | } 14 | 15 | // Firefox 16 | @-moz-keyframes progress-bar-stripes { 17 | from { background-position: 40px 0; } 18 | to { background-position: 0 0; } 19 | } 20 | 21 | // IE9 22 | @-ms-keyframes progress-bar-stripes { 23 | from { background-position: 40px 0; } 24 | to { background-position: 0 0; } 25 | } 26 | 27 | // Opera 28 | @-o-keyframes progress-bar-stripes { 29 | from { background-position: 0 0; } 30 | to { background-position: 40px 0; } 31 | } 32 | 33 | // Spec 34 | @keyframes progress-bar-stripes { 35 | from { background-position: 40px 0; } 36 | to { background-position: 0 0; } 37 | } 38 | 39 | 40 | 41 | // THE BARS 42 | // -------- 43 | 44 | // Outer container 45 | .progress { 46 | overflow: hidden; 47 | height: @baseLineHeight; 48 | margin-bottom: @baseLineHeight; 49 | #gradient > .vertical(#f5f5f5, #f9f9f9); 50 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); 51 | .border-radius(@baseBorderRadius); 52 | } 53 | 54 | // Bar of progress 55 | .progress .bar { 56 | width: 0%; 57 | height: 100%; 58 | color: @white; 59 | float: left; 60 | font-size: 12px; 61 | text-align: center; 62 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 63 | #gradient > .vertical(#149bdf, #0480be); 64 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 65 | .box-sizing(border-box); 66 | .transition(width .6s ease); 67 | } 68 | .progress .bar + .bar { 69 | .box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)"); 70 | } 71 | 72 | // Striped bars 73 | .progress-striped .bar { 74 | #gradient > .striped(#149bdf); 75 | .background-size(40px 40px); 76 | } 77 | 78 | // Call animation for the active one 79 | .progress.active .bar { 80 | -webkit-animation: progress-bar-stripes 2s linear infinite; 81 | -moz-animation: progress-bar-stripes 2s linear infinite; 82 | -ms-animation: progress-bar-stripes 2s linear infinite; 83 | -o-animation: progress-bar-stripes 2s linear infinite; 84 | animation: progress-bar-stripes 2s linear infinite; 85 | } 86 | 87 | 88 | 89 | // COLORS 90 | // ------ 91 | 92 | // Danger (red) 93 | .progress-danger .bar, .progress .bar-danger { 94 | #gradient > .vertical(#ee5f5b, #c43c35); 95 | } 96 | .progress-danger.progress-striped .bar, .progress-striped .bar-danger { 97 | #gradient > .striped(#ee5f5b); 98 | } 99 | 100 | // Success (green) 101 | .progress-success .bar, .progress .bar-success { 102 | #gradient > .vertical(#62c462, #57a957); 103 | } 104 | .progress-success.progress-striped .bar, .progress-striped .bar-success { 105 | #gradient > .striped(#62c462); 106 | } 107 | 108 | // Info (teal) 109 | .progress-info .bar, .progress .bar-info { 110 | #gradient > .vertical(#5bc0de, #339bb9); 111 | } 112 | .progress-info.progress-striped .bar, .progress-striped .bar-info { 113 | #gradient > .striped(#5bc0de); 114 | } 115 | 116 | // Warning (orange) 117 | .progress-warning .bar, .progress .bar-warning { 118 | #gradient > .vertical(lighten(@orange, 15%), @orange); 119 | } 120 | .progress-warning.progress-striped .bar, .progress-striped .bar-warning { 121 | #gradient > .striped(lighten(@orange, 15%)); 122 | } 123 | -------------------------------------------------------------------------------- /book/bootstrap/less/responsive-1200px-min.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Large desktop and up 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 1200px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200); 16 | 17 | // Thumbnails 18 | .thumbnails { 19 | margin-left: -@gridGutterWidth1200; 20 | } 21 | .thumbnails > li { 22 | margin-left: @gridGutterWidth1200; 23 | } 24 | .row-fluid .thumbnails { 25 | margin-left: 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /book/bootstrap/less/responsive-768px-979px.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Tablet to desktop 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 768px) and (max-width: 979px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth768, @gridGutterWidth768); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth768, @gridGutterWidth768); 16 | 17 | // No need to reset .thumbnails here since it's the same @gridGutterWidth 18 | 19 | } 20 | -------------------------------------------------------------------------------- /book/bootstrap/less/responsive-utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // IE10 Metro responsive 7 | // Required for Windows 8 Metro split-screen snapping with IE10 8 | // Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ 9 | @-ms-viewport{ 10 | width: device-width; 11 | } 12 | 13 | // Hide from screenreaders and browsers 14 | // Credit: HTML5 Boilerplate 15 | .hidden { 16 | display: none; 17 | visibility: hidden; 18 | } 19 | 20 | // Visibility utilities 21 | 22 | // For desktops 23 | .visible-phone { display: none !important; } 24 | .visible-tablet { display: none !important; } 25 | .hidden-phone { } 26 | .hidden-tablet { } 27 | .hidden-desktop { display: none !important; } 28 | .visible-desktop { display: inherit !important; } 29 | 30 | // Tablets & small desktops only 31 | @media (min-width: 768px) and (max-width: 979px) { 32 | // Hide everything else 33 | .hidden-desktop { display: inherit !important; } 34 | .visible-desktop { display: none !important ; } 35 | // Show 36 | .visible-tablet { display: inherit !important; } 37 | // Hide 38 | .hidden-tablet { display: none !important; } 39 | } 40 | 41 | // Phones only 42 | @media (max-width: 767px) { 43 | // Hide everything else 44 | .hidden-desktop { display: inherit !important; } 45 | .visible-desktop { display: none !important; } 46 | // Show 47 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior 48 | // Hide 49 | .hidden-phone { display: none !important; } 50 | } 51 | 52 | // Print utilities 53 | .visible-print { display: none !important; } 54 | .hidden-print { } 55 | 56 | @media print { 57 | .visible-print { display: inherit !important; } 58 | .hidden-print { display: none !important; } 59 | } 60 | -------------------------------------------------------------------------------- /book/bootstrap/less/responsive.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.3.2 3 | * 4 | * Copyright 2013 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world by @mdo and @fat. 9 | */ 10 | 11 | 12 | // Responsive.less 13 | // For phone and tablet devices 14 | // ------------------------------------------------------------- 15 | 16 | 17 | // REPEAT VARIABLES & MIXINS 18 | // ------------------------- 19 | // Required since we compile the responsive stuff separately 20 | 21 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 22 | @import "mixins.less"; 23 | 24 | 25 | // RESPONSIVE CLASSES 26 | // ------------------ 27 | 28 | @import "responsive-utilities.less"; 29 | 30 | 31 | // MEDIA QUERIES 32 | // ------------------ 33 | 34 | // Large desktops 35 | @import "responsive-1200px-min.less"; 36 | 37 | // Tablets to regular desktops 38 | @import "responsive-768px-979px.less"; 39 | 40 | // Phones to portrait tablets and narrow desktops 41 | @import "responsive-767px-max.less"; 42 | 43 | 44 | // RESPONSIVE NAVBAR 45 | // ------------------ 46 | 47 | // From 979px and below, show a button to toggle navbar contents 48 | @import "responsive-navbar.less"; 49 | -------------------------------------------------------------------------------- /book/bootstrap/less/scaffolding.less: -------------------------------------------------------------------------------- 1 | // 2 | // Scaffolding 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body reset 7 | // ------------------------- 8 | 9 | body { 10 | margin: 0; 11 | font-family: @baseFontFamily; 12 | font-size: @baseFontSize; 13 | line-height: @baseLineHeight; 14 | color: @textColor; 15 | background-color: @bodyBackground; 16 | } 17 | 18 | 19 | // Links 20 | // ------------------------- 21 | 22 | a { 23 | color: @linkColor; 24 | text-decoration: none; 25 | } 26 | a:hover, 27 | a:focus { 28 | color: @linkColorHover; 29 | text-decoration: underline; 30 | } 31 | 32 | 33 | // Images 34 | // ------------------------- 35 | 36 | // Rounded corners 37 | .img-rounded { 38 | .border-radius(6px); 39 | } 40 | 41 | // Add polaroid-esque trim 42 | .img-polaroid { 43 | padding: 4px; 44 | background-color: #fff; 45 | border: 1px solid #ccc; 46 | border: 1px solid rgba(0,0,0,.2); 47 | .box-shadow(0 1px 3px rgba(0,0,0,.1)); 48 | } 49 | 50 | // Perfect circle 51 | .img-circle { 52 | .border-radius(500px); // crank the border-radius so it works with most reasonably sized images 53 | } 54 | -------------------------------------------------------------------------------- /book/bootstrap/less/thumbnails.less: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files 7 | 8 | // Make wrapper ul behave like the grid 9 | .thumbnails { 10 | margin-left: -@gridGutterWidth; 11 | list-style: none; 12 | .clearfix(); 13 | } 14 | // Fluid rows have no left margin 15 | .row-fluid .thumbnails { 16 | margin-left: 0; 17 | } 18 | 19 | // Float li to make thumbnails appear in a row 20 | .thumbnails > li { 21 | float: left; // Explicity set the float since we don't require .span* classes 22 | margin-bottom: @baseLineHeight; 23 | margin-left: @gridGutterWidth; 24 | } 25 | 26 | // The actual thumbnail (can be `a` or `div`) 27 | .thumbnail { 28 | display: block; 29 | padding: 4px; 30 | line-height: @baseLineHeight; 31 | border: 1px solid #ddd; 32 | .border-radius(@baseBorderRadius); 33 | .box-shadow(0 1px 3px rgba(0,0,0,.055)); 34 | .transition(all .2s ease-in-out); 35 | } 36 | // Add a hover/focus state for linked versions only 37 | a.thumbnail:hover, 38 | a.thumbnail:focus { 39 | border-color: @linkColor; 40 | .box-shadow(0 1px 4px rgba(0,105,214,.25)); 41 | } 42 | 43 | // Images and captions 44 | .thumbnail > img { 45 | display: block; 46 | max-width: 100%; 47 | margin-left: auto; 48 | margin-right: auto; 49 | } 50 | .thumbnail .caption { 51 | padding: 9px; 52 | color: @gray; 53 | } 54 | -------------------------------------------------------------------------------- /book/bootstrap/less/tooltip.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltips 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .tooltip { 8 | position: absolute; 9 | z-index: @zindexTooltip; 10 | display: block; 11 | visibility: visible; 12 | font-size: 11px; 13 | line-height: 1.4; 14 | .opacity(0); 15 | &.in { .opacity(80); } 16 | &.top { margin-top: -3px; padding: 5px 0; } 17 | &.right { margin-left: 3px; padding: 0 5px; } 18 | &.bottom { margin-top: 3px; padding: 5px 0; } 19 | &.left { margin-left: -3px; padding: 0 5px; } 20 | } 21 | 22 | // Wrapper for the tooltip content 23 | .tooltip-inner { 24 | max-width: 200px; 25 | padding: 8px; 26 | color: @tooltipColor; 27 | text-align: center; 28 | text-decoration: none; 29 | background-color: @tooltipBackground; 30 | .border-radius(@baseBorderRadius); 31 | } 32 | 33 | // Arrows 34 | .tooltip-arrow { 35 | position: absolute; 36 | width: 0; 37 | height: 0; 38 | border-color: transparent; 39 | border-style: solid; 40 | } 41 | .tooltip { 42 | &.top .tooltip-arrow { 43 | bottom: 0; 44 | left: 50%; 45 | margin-left: -@tooltipArrowWidth; 46 | border-width: @tooltipArrowWidth @tooltipArrowWidth 0; 47 | border-top-color: @tooltipArrowColor; 48 | } 49 | &.right .tooltip-arrow { 50 | top: 50%; 51 | left: 0; 52 | margin-top: -@tooltipArrowWidth; 53 | border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0; 54 | border-right-color: @tooltipArrowColor; 55 | } 56 | &.left .tooltip-arrow { 57 | top: 50%; 58 | right: 0; 59 | margin-top: -@tooltipArrowWidth; 60 | border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth; 61 | border-left-color: @tooltipArrowColor; 62 | } 63 | &.bottom .tooltip-arrow { 64 | top: 0; 65 | left: 50%; 66 | margin-left: -@tooltipArrowWidth; 67 | border-width: 0 @tooltipArrowWidth @tooltipArrowWidth; 68 | border-bottom-color: @tooltipArrowColor; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /book/bootstrap/less/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Quick floats 7 | .pull-right { 8 | float: right; 9 | } 10 | .pull-left { 11 | float: left; 12 | } 13 | 14 | // Toggling content 15 | .hide { 16 | display: none; 17 | } 18 | .show { 19 | display: block; 20 | } 21 | 22 | // Visibility 23 | .invisible { 24 | visibility: hidden; 25 | } 26 | 27 | // For Affix plugin 28 | .affix { 29 | position: fixed; 30 | } 31 | -------------------------------------------------------------------------------- /book/bootstrap/less/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @wellBackground; 12 | border: 1px solid darken(@wellBackground, 7%); 13 | .border-radius(@baseBorderRadius); 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-large { 23 | padding: 24px; 24 | .border-radius(@borderRadiusLarge); 25 | } 26 | .well-small { 27 | padding: 9px; 28 | .border-radius(@borderRadiusSmall); 29 | } 30 | -------------------------------------------------------------------------------- /book/epub.css: -------------------------------------------------------------------------------- 1 | /* incut styles */ 2 | 3 | div.incut { 4 | margin-left: 1em; 5 | } 6 | .incut-DEF { 7 | background-color: #f2f2f2; 8 | } 9 | .incut-WARN { 10 | background-color: #ffccaa; 11 | } 12 | .incut-DANGER { 13 | background-color: #f4d7d7; 14 | } 15 | .incut-NOTE { 16 | background-color: #aaeeff; 17 | border-left: blue 3px solid; 18 | } 19 | .incut-INFO { 20 | background-color: #d5e5ff; 21 | } 22 | 23 | 24 | /* images */ 25 | img { 26 | width: 100%; 27 | } -------------------------------------------------------------------------------- /book/hints/ex1.md: -------------------------------------------------------------------------------- 1 | ### Exercise 1 2 | 3 | This exercise is intended to learn some features of dynamic tracing languages that was discussed in modules 1 and 2. First of all we need to pick probes that we will use in our tracing script. They would be parts of `syscall` provider/tapset. As you can remember from [stap command options][tools/systemtap#stap], probe parameters can be checked with `-L` option: 4 | ``` 5 | # stap -L 'syscall.open' 6 | syscall.open name:string filename:string flags:long mode:long argstr:string $filename:long int $flags:long int $mode:long int 7 | # stap -L 'syscall.open.return' 8 | syscall.open.return name:string retstr:string $return:long int $filename:long int $flags:long int $mode:long int 9 | ``` 10 | Same can be done for dtrace with `-l` option: 11 | ``` 12 | # dtrace -l -f openat\* -v 13 | ID PROVIDER MODULE FUNCTION NAME 14 | 14167 syscall openat entry 15 | ... 16 | ``` 17 | 18 | Return value (which would represent file descriptor number) will be saved into `$return` variable in SystemTap and `arg1` argument in DTrace. We will also need flags values: `arg2` in DTrace (because they are going third in `openat()` prototype). In SystemTap you can use either DWARF variable `$flags` or tapset variable `flags`. Latter is more stable. 19 | 20 | Similarly, path to opened file will be passed as second `openat()` argument and will be available in DTrace as `arg1` or `$filename`/`filename` in SystemTap. At the moment of system call, however, file path will be a string which is located in user address space, so to get it in tracing script, you will need to copy it by using `copyinstr()` in DTrace or one of `user_string*()` functions. Tapset variable already uses `user_string_quoted()` to access variable, so we will use it in our scripts. 21 | 22 | Note that data that we want gather is available in two different probes: flags and file path are provided by entry probe, while file descriptor number can only be collected in return probe (SystemTap can provide flags and file path in return probe, but as we mentioned, it depends on compiler optimizations). Since both probes will be executed in the same context, we can use [thread-local variables][lang/vars#thread-local-vars]. 23 | 24 | Finally, stringifying flags will require usage of ternary operator `?:` in DTrace or `if/else` construct in SystemTap. To concatenate strings, use `strjoin` from DTrace or string concatenation operator `.` in SystemTap. 25 | 26 | Here are resulting DTrace script which implements required functionality: 27 | 28 | ````` scripts/dtrace/opentrace.d 29 | 30 | I have used `sprintf()` to concatenate strings in SystemTap version of a script: 31 | 32 | ````` scripts/stap/opentrace.stp 33 | 34 | Finally, you will need to add predicates to compare paths with `/etc` in DTrace by using `strstr()` subroutine and comparing it with `0` and SystemTap's `ininstr()` function. 35 | -------------------------------------------------------------------------------- /book/hints/ex2.md: -------------------------------------------------------------------------------- 1 | ### Exercise 2 2 | 3 | We will have to use `count()` [aggregation][lang/assocarr#aggr] to count `open()` and `openat()` system calls. It can be cleaned up from outdated data with `trunc()` action in DTrace or `delete` operation in SystemTap. These scripts are roughly based on `wstat.d` and `wstat.stp` from [aggregations example][lang/assocarr#aggr-example]. 4 | 5 | To print current timestamp we can use `%Y` formatting specifier and `walltimestamp` variable in DTrace. Same can be achieved with `ctime()` and `gettimeofday_s()` functions from SystemTap. To print data periodically, we can use [timer probes][lang/probes#timers]: `timer.s($1)` in SystemTap or `tick-$1s` from DTrace. `$1` here represents first command line argument. 6 | 7 | Finally, we need to determine if open operation requests creation of file or not. We should write predicate for that which tests flags passed to open for `O_CREAT` flag (we have learned how to access flags in previous exercise). 8 | 9 | Here are resulting scripts: 10 | 11 | ````` scripts/dtrace/openaggr.d 12 | ````` scripts/stap/openaggr.stp 13 | 14 | -------------------------------------------------------------------------------- /book/hints/ex3.md: -------------------------------------------------------------------------------- 1 | ### Exercise 3 2 | 3 | #### Part 1 4 | 5 | In the first part of this exercise we will need to check which fields of `struct task_struct` in Linux or `proc_t` are responsible for which aspects of process functioning. You will need to apply following changes to dump task scripts: timer probe has to be replaced to a pair of probes: `proc:::exec-*` and `proc:::exit` in DTrace or `kprocess.exec_complete` and `kprocess.exit` in SystemTap. We have used exit probes for `execve()` system call to collect command line arguments: they are not filled in unless `execve()` call finishes. 6 | 7 | Here list of expected observations during this exercise: 8 | 9 | * When you run program with extra argument, it will be cleared in `main()` function, so you will see original argument in exec-probe, but only 'X' letters when program exits. 10 | * When you run program through symbolic link `lab3-1`, VFS node which refer to a binary file will point to a regular file `lab3`. That node is represented by `p_exec` field of `proc_t` in Solaris or `exe_file` of `task_struct` in Linux. `execname`, however, will behave differently in Solaris and Linux. 11 | * When you run program in chroot environment, root process directory will change from `/` to `/tmp/chroot`. 12 | 13 | Here are resulting scripts (they are not much different from original): 14 | 15 | ````` scripts/stap/dumptask-lab3.stp 16 | ````` scripts/dtrace/dumptask-lab3.d 17 | 18 | #### Part 2 19 | 20 | First of all we have to create several associative arrays which will use PID as a key (we can't use thread-local variables here because `exit()` can be called from any of process threads), and timestamp as a value. Final data will be kept in aggregations which we already learned in exercise 2. 21 | 22 | We will use probes from the section _Lifetime of a process_. They are shown in the following picture: 23 | 24 | ![image:forkproc](forkproc.png) 25 | 26 | However, we do not know PID at the time `fork()` is called so we will use thread-local variable for that. We can check return value of `fork()` in return probe and re-use timestamp saved previously if everything went fine and `fork()` has returned value greater than 1 or throw it away. 27 | 28 | We wrote an ugly function `task_args()` to collect process arguments in `dumptask.stp` script. This data is available since SystemTap 2.5: `kprocess.exec` probe provides program's arguments in `argstr` argument. We will use `curpsinfo->pr_psargs` on Solaris as it keeps first 80 characters of command line to get rid of copying userspace arguments too. We will use `timestamp` variable in DTrace as a source of timestamps (again, a tautology). We will use `local_clock_us()` function as we do not care about CPU time skew. 29 | 30 | Finally, to reduce memory footprint in SystemTap, we will reduce associative arrays sizes. Here are resulting scripts: 31 | 32 | ````` scripts/stap/forktime.stp 33 | ````` scripts/dtrace/forktime.d 34 | 35 | -------------------------------------------------------------------------------- /book/hints/ex5.md: -------------------------------------------------------------------------------- 1 | ### Exercise 5 2 | 3 | We will need to access pointer to `block_device` pointer in Linux to collect block device name and group data by it. As we know, main filesystem structure is called `super_block` which contains `s_bdev` pointer which has `struct block_device*`. SystemTap has two tapset functions, `MINOR()` and `MAJOR()` which allow to extract device number from `bd_dev` field of that structure. There is also an undocumented `bdevname()` function which is more convenient as it returns string, so we will use it. 4 | 5 | We will attach probes to `vfs_write()` and `vfs_read()` functions to trace filesystem operations. First argument of that functions is pointer to file of type `struct file*`. Amount of data being written or read is passed through argument `$count`. 6 | 7 | BIO level can be traced with `ioblock` tapset. We will do so by using its `ioblock.request` probe. It has following arguments: `bdev` -- `block_device` pointer, `size` -- amount of data in request, `rw` -- read or write flag which can be tested for equality with `BIO_READ` or `BIO_WRITE` constants. 8 | 9 | Here is resulting `deblock.stp` script: 10 | 11 | ````` scripts/stap/deblock.stp 12 | 13 | To trace readahead from part 2 we will need to replace `vfs_write` to `vfs_read`, `BIO_WRITE` to `BIO_READ` and get rid from amount of data in request saving into aggregation by replacing it with number of requests (which will be constant `1`). 14 | 15 | We can use `scsi.ioentry` probe to trace SCSI operations. We can actually detect which command was used by parsing CDB buffer, but we will omit that and will trace all SCSI operations. Getting device name, however is not that easy: `request` structure which is used in SCSI stack refers to `gendisk` and `hd_struct` structures, but they won't refer to `block_device` (on contrary, `block_device` itself refers them). So we will make a small trick: there is a linked list of structures `bio` ... `biotail` which refer block device structure the same way they do in BIO probes, so we will simply copy approach from `ioblock.request` probe. 16 | 17 | We will get `readahead.stp` script after applying all these modifications: 18 | 19 | ````` scripts/stap/readahead.stp 20 | 21 | One can get device name with `ddi_pathname()` action or `devinfo_t` translator (which uses it indirectly) which is applied to `buf` structure. Probes from `io` provider will do it automatically by passing resulting pseudo-structure as `args[1]` argument. Aside from name, it contains minor and major device names. 22 | 23 | Getting device name on VFS layer, however is harder: `vfs_t` structure which describes filesystem has only device number `vfs_dev`. ZFS makes things even harder: there is intermediate layer called pool which hides block devices from filesystem layer. So we will use mountpoints as an aggregation key. We will trace filesystem operations by attaching to `fop_read()` and `fop_write()` which accept pointers to `vnode_t` of file as their first argument and pointer to `uio` structure as their second argument (it describes user request and thus contains amount of data). 24 | 25 | Using all this we can get our `deblock.d` script: 26 | 27 | ````` scripts/dtrace/deblock.d 28 | 29 | We will trace SCSI stack by attaching to `scsi-transport-dispatch` probe which will receive pointer to `buf` as first argument. That is very similar to probes from `io` provider except that probe doesn't apply translators on buffer. 30 | 31 | Other changes in `readahead.d` are similar to those that was done for SystemTap: 32 | 33 | ````` scripts/dtrace/readahead.d 34 | -------------------------------------------------------------------------------- /book/hints/ex6.md: -------------------------------------------------------------------------------- 1 | ### Exercise 6 2 | 3 | This exercise is not so different than any [latency measurement][principles/perf#latency] script where latency is measured as difference between timestamps of two probe firings and saved to an aggregation. 4 | 5 | Note that `plockstat$` provider doesn't serve a probe for mutex lock attempt, so we had to expand it by using `pid$` provider. As you may notice from ustack outputs in `pthread.d` example, mutex lock attempts are implemented by `mutex_lock_impl()` libc function. We will use `quantize()` aggregation which will be printed with `printa`: 6 | 7 | ````` scripts/dtrace/mtxtime.d 8 | 9 | You will need to bind this script to tsexperiment process using `-c` or `-p` option. 10 | 11 | We will need to use static probes `mutex_entry` and `mutex_acquired` for a SystemTap version of that script. However, we will need to be careful while working with userspace backtraces. First of all, we should use `-d` option to provide path to SystemTap for resolving symbols or `--ldd` to make it scan library dependencies of traced binary and automatically add them (when some of them are missing, `stap` utility will provide a hint with full paths). 12 | 13 | Mutexes are also often used in TSLoad which can cause excessive overheads when we try to trace them, especially when we will use `ubacktrace()` function. You can use `STP_NO_OVERLOAD` macro definition (which can be passed to `stap` with `-D` option) to prevent stap from failing when overheads are big, or you can reduce overheads. In our case we will limit amount of traced callers by using `ucallers()` function which accepts depth of backtrace as a first argument like `ustack()` function from backtrace and only collects addresses without resolving them to symbols. We will defer symbol resolving to an aggregation printing. 14 | 15 | Here are our script for SystemTap: 16 | 17 | ````` scripts/stap/mtxtime.stp 18 | -------------------------------------------------------------------------------- /book/hints/ex7.md: -------------------------------------------------------------------------------- 1 | ### Exercise 7 2 | 3 | Nature of solution of this exercise depends on your Apache and PHP configuration. In our case (as described in [lab description][lab/web]), PHP was built as Apache HTTPD module (by using `--with-apxs2` option of `configure`-script), so all PHP code will be executed in a context of Apache worker, so we can safely use Thread-Local variables. If PHP was deployed with PHP-FPM, things would be more complicated. 4 | 5 | So we will need to use `process-request-entry` probe to get URI of request and pair of probes `function-entry`/`function-entry` to measure execution time of a function. Since name of a method is passed in multiple probe arguments, we will have to use string concatenation. Like in many other exercises, we will use aggregations to collect statistics. Note that you can use PHP probe `request-startup` instead of `process-request-entry`. 6 | 7 | As you could remember from a [profiling][principles/profiling] section of tracing principles, generally you shouldn't measure execution time of a function by tracing entry and exit points of it. However, PHP is an interpreted language, so it has lesser relative overheads of tracing because execution of its opcodes is slower than for the real processor (unless you are using some precompiler to machine language like HHVM) and we can afford full-tracing of it. 8 | 9 | Here are resulting implementations of scripts for SystemTap and DTrace: 10 | 11 | ````` scripts/stap/topphp.stp 12 | ````` scripts/dtrace/topphp.d 13 | -------------------------------------------------------------------------------- /book/index.md: -------------------------------------------------------------------------------- 1 | [__endfrontpage__] 2 | 3 | Copyright © 2011-2016 Sergey Klyaus 4 | 5 | This work is licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 3.0 License. To view a copy of this license, visit [https://creativecommons.org/licenses/by-nc-sa/3.0/](https://creativecommons.org/licenses/by-nc-sa/3.0/) or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. 6 | 7 | [__endbackpage__] 8 | 9 | Other formats: [PDF (cheatsheets)](dtrace-stap-cheatsheet.pdf), [PDF (full)](dtrace-stap-book.pdf), [PDF (no sources)](dtrace-stap-book-ns.pdf), [EPUB (no sources)](dtrace-stap-book.epub) 10 | 11 | ### Introduction 12 | [__docspace__:intro] 13 | 14 | * [Foreword][intro/foreword] 15 | * [Typographic conventions][intro/conv] 16 | * [TSLoad workload generator][intro/tsload] 17 | * [Operating system Kernel][intro/oskernel] 18 | 19 | ### Module 1: Dynamic tracing tools. dtrace and stap tools 20 | [__docspace__:tools] 21 | 22 | * [Tracing][tools/tracing] 23 | * [Dynamic tracing][tools/dyntrace] 24 | * [DTrace][tools/dtrace] 25 | * [SystemTap][tools/systemtap] 26 | * [Safety and errors][tools/safety] 27 | * [Stability][tools/stability] 28 | 29 | ### Module 2: Dynamic tracing languages 30 | [__docspace__:lang] 31 | 32 | * [Introduction][lang/intro] 33 | * [Probes][lang/probes] 34 | * [Arguments][lang/args] 35 | * [Context][lang/context] 36 | * [Predicates][lang/predicates] 37 | * [Types and Variables][lang/vars] 38 | * [Pointers][lang/pointers] 39 | * [Strings and Structures][lang/strstr] 40 | * [Exercise 1][lang/ex1] 41 | * [Associative arrays and aggregations][lang/assocarr] 42 | * [Time][lang/time] 43 | * [Printing and speculations][lang/print] 44 | * [Tapsets & translators][lang/tapset] 45 | * [Exercise 2][lang/ex2] 46 | 47 | ### Module 3: Principles of dynamic tracing 48 | [__docspace__:principles] 49 | 50 | * [Applying tracing][principles/apply] 51 | * [Dynamic code analysis][principles/dyncode] 52 | * [Profiling][principles/profiling] 53 | * [Performance analysis][principles/perf] 54 | * [Pre- and post-processing][principles/prepost] 55 | * [Vizualization][principles/viz] 56 | 57 | ### Module 4: Operating system kernel tracing 58 | [__docspace__:kernel] 59 | 60 | * [Process management][kernel/proc] 61 | * [Exercise 3][kernel/ex3] 62 | * [Process scheduler][kernel/sched] 63 | * [Virtual Memory][kernel/virtmem] 64 | * [Exercise 4][kernel/ex4] 65 | * [Virtual File System][kernel/fs] 66 | * [Block Input-Output][kernel/bio] 67 | * [Asynchronicity in kernel][kernel/async] 68 | * [Exercise 5][kernel/ex5] 69 | * [Network Stack][kernel/net] 70 | * [Synchronization primitives][kernel/sobj] 71 | * [Interrupt handling and deferred execution][kernel/irq] 72 | 73 | ### Module 5: Application tracing 74 | [__docspace__:app] 75 | 76 | * [Userspace process tracing][app/proc] 77 | * [Unix C library][app/libc] 78 | * [Exercise 6][app/ex6] 79 | * [Java Virtual Machine][app/java] 80 | * [Non-native languages][app/interp] 81 | * [Web applications][app/web] 82 | * [Exercise 7][app/ex7] 83 | 84 | ### Appendix A. Exercise hints and solutions 85 | [__docspace__:hints] 86 | 87 | * [Exercise 1][hints/ex1] 88 | * [Exercise 2][hints/ex2] 89 | * [Exercise 3][hints/ex3] 90 | * [Exercise 4][hints/ex4] 91 | * [Exercise 5][hints/ex5] 92 | * [Exercise 6][hints/ex6] 93 | * [Exercise 7][hints/ex7] 94 | 95 | ### Appendix B. Lab setup 96 | [__docspace__:lab] 97 | 98 | * [Setting up Operating Systems][lab/os] 99 | * [iSCSI][lab/iscsi] 100 | * [Web application stack][lab/web] 101 | 102 | ### Appendix C. [Cheatsheet][cheatsheet/cheatsheet] 103 | [__docspace__:cheatsheet] 104 | -------------------------------------------------------------------------------- /book/intro/experiment.json: -------------------------------------------------------------------------------- 1 | { "name": "jump_table", 2 | "steps": { 3 | "jt": { 4 | "num_steps": 100, 5 | "num_requests": 2000 } 6 | }, 7 | "threadpools": { 8 | "tp_jt" : { 9 | "num_threads": 24, 10 | "quantum": 2000000000, 11 | "disp": { "type": "round-robin" } } 12 | }, 13 | "workloads" : { 14 | "jt" : { 15 | "wltype": "jt", 16 | "threadpool": "tp_jt", 17 | "params": { 18 | "num_request_types": 5000, 19 | "request_type": { 20 | "randgen": { "class": "lcg" } }, 21 | "is_incorrect": { 22 | "randgen": { "class": "lcg" }, 23 | "pmap": [ 24 | { "probability": 0.2, "value": true }, 25 | { "probability": 0.8, "value": false } 26 | ] } }, 27 | "rqsched": { 28 | "type": "iat", 29 | "distribution": "exponential" 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /book/intro/oskernel.md: -------------------------------------------------------------------------------- 1 | ### Operating System Kernel 2 | 3 | !!! DEF 4 | According to Wikipedia, [Operating System Kernel](http://en.wikipedia.org/wiki/Kernel_%28operating_system%29) is 5 | 6 | > a computer program that manages I/O (input/output) requests from software, and translates them into data processing instructions for the central processing unit and other electron components of a computer. The kernel is a fundamental part of a modern computer's operating system. 7 | 8 | !!! 9 | 10 | We will refer to operating system kernel as _kernel_ in the rest of the book. Applications are using _system call_ mechanism to access various kernel functions, and by doing that they transfer control to kernel routines. The current state of application including all variables and current _program counter_ is called _context_. C is a programming language which is vastly used for writing Unix-like operating systems kernels such as Solaris, FreeBSD and Linux. C supports only procedural programming, but kernel developers adopted object-oriented and even functional programming. 11 | 12 | Where can we get information on kernel? Like I said, the most reliable source of such information is source codes which contain comments. You can use cross-reference tools to navigate source codes as easy as click a hyperlink. Some of them are publicly available: like [lxr.linux.no](http://lxr.linux.no/) which contains Linux source and [src.illumos.org](http://src.illumos.org/) which contains sources for Illumos (FOSS fork of OpenSolaris) in project illumos-gate. You can create your own cross-reference with OpenGrok tool: [https://github.com/OpenGrok/OpenGrok](https://github.com/OpenGrok/OpenGrok). 13 | 14 | Of course we have to mention textual sources of information. For Linux it is: 15 | 16 | * `Documentation/` directory in kernel sources 17 | * [Linux Kernel Mailing List](http://lkml.org/) 18 | * [Linux info from source](http://lwn.net/) 19 | * Robert Love book "Linux Kernel Development" 20 | * [Linux Device Drivers Book](https://lwn.net/Kernel/LDD3/) 21 | 22 | Some sources about Solaris: 23 | 24 | * [solaris.java.net](http://solaris.java.net/) -- remnants of old OpenSolaris site 25 | * Richard McDougall and Jim Mauro book "Solaris(TM) Internals: Solaris 10 and OpenSolaris Kernel Architecture" 26 | * Oracle course "Solaris 10 Operating System Internals" 27 | 28 | !!! WARN 29 | Solaris sources was closed after Oracle acquisition of Sun in 2009 and some information on Solaris may be outdated. 30 | !!! 31 | -------------------------------------------------------------------------------- /book/intro/tsload.md: -------------------------------------------------------------------------------- 1 | ### [__index__:TSLoad workload generator] TSLoad workload generator 2 | 3 | During this course we will need to demonstrate created scripts on a real system. We will use version 0.2 of TSLoad workload generator to do so. Its [documentation](http://myaut.github.io/tsload/) and [source code](https://github.com/myaut/tsload) are available on GitHub. 4 | 5 | Experiment configuration files are kept in JSON format: each experiment starts with directory with `experiment.json` file in it (it can also be accompanied by traces and timeseries). This file contains description of threadpools and workloads: their types and parameters. 6 | 7 | ````` book/intro/experiment.json 8 | 9 | For example, [ref:experiment.json] defines an experiment called `jump_table`. `workloads` section defines workload `jt` which type is also `jt`. That workload have the following parameters: 10 | 11 | * `num_request_types` - set globally for entire workload - number of "request types" that will be generated; 12 | * `request_types` - generated for each request with linear congruential PRNG; 13 | * `is_incorrect` - boolean value which will be set to true for 20% requests. 14 | 15 | It also defines _request scheduler_ -- inter arrival time will be generated using exponential distribution. `steps` section defines number of requests which will be generated for this workload: 100 steps with 2000 requests in each. 16 | 17 | `threadpools` section defines threadpools which will perform our workloads. It defines pool `tp_jt` which contains 24 threads with step period set to 2 second (as paramter `quantum` sets in nanoseconds). _Threadpool dispatcher_ describes how requests will be distributed across threads and it is set to round-robin. 18 | 19 | If we try to draw a timing diagram of the requests generated by this configuration file we will get something like [image:tsload]. 20 | 21 | ![image:tsload](tsload.png) 22 | 23 | `jt` workload type is defined in a separate loadable module which contains code for simulating requests. During our book we meet similar modules in exercises: `proc_starter` which forks processes, `file_opener` which randomly opens files and other modules. 24 | 25 | Experiment is started with `tseexperiment` command: 26 | ``` 27 | # tsexperiment -e /path/to/experiment run 28 | ``` 29 | 30 | In this command `/path/to/experiment` is a directory which contains file `experiment.json`. That directory will also contain experiment results which can be listed with `list` subcommand of `tseexperiment`: 31 | ``` 32 | # tsexperiment -e /path/to/experiment list 33 | ``` 34 | 35 | Results may be exported to CSV format with `export` subcommand or some statistics may be shown with `report` subcommand. 36 | 37 | It is not necessary to edit configuration file each time parameter have to be altered: `run` subcommand has `-s` option. To provide its argument, check flattened names of configuration parameters with `-l` option of subcommand `show`: 38 | ``` 39 | # tsexperiment -e /opt/tsload/var/tsload/mbench/jt show -l 40 | name=jump_table 41 | steps:jt:num_steps=100 42 | steps:jt:num_requests=2000 43 | ... 44 | ``` 45 | 46 | So, to change number of per-step requests to 500, you should call `tsexperiment` with following options: 47 | ``` 48 | # tsexperiment -e /opt/tsload/var/tsload/mbench/jt run \ 49 | -s steps:jt:num_requests=500 50 | ``` 51 | 52 | In some cases we will need to use hardware device names in experiment configuration, i.e. to bind threads to CPU cores. To get their names, run `tshostinfo` command: 53 | ``` 54 | tshostinfo -x 55 | ``` -------------------------------------------------------------------------------- /book/kernel/ex3.md: -------------------------------------------------------------------------------- 1 | ### Exercise 3 2 | 3 | #### Part 1 4 | 5 | Modify `dumptask.stp` and `dumptask.d` so it will print information on successful binary load by `execve()` and before process exit. Write a simple program `lab3.c`: 6 | 7 | ````` scripts/src/lab3.c 8 | 9 | Compile it with GCC: 10 | ``` 11 | # gcc lab3.c -o lab3 12 | ``` 13 | 14 | Run changed scripts and run your program in different ways: 15 | 16 | * Run it with argument: 17 | ``` 18 | # ./lab3 arg1 19 | ``` 20 | * Create a symbolic link and run a program through it: 21 | ``` 22 | # ln -s lab3 lab3-1 23 | # ./lab3-1 24 | ``` 25 | * Created chrooted environment and run `lab3` inside it: 26 | ``` 27 | # mkdir -p /tmp/chroot/bin /tmp/chroot/lib64 /tmp/chroot/lib 28 | # mount --bind /lib64 /tmp/chroot/lib64 (in Linux) 29 | # mount -F lofs /lib /tmp/chroot/lib (in Solaris) 30 | # cp lab3 /tmp/chroot/bin 31 | # chroot /tmp/chroot/ /bin/lab3 32 | ``` 33 | 34 | __Q__: What data output has been changed? Try to explain these changes. 35 | 36 | #### Part 2 37 | 38 | Shell scripts have overhead caused by need to spawn new processes for basic operations, and thus calling `fork()` and `execve()`. Write SystemTap and DTrace scripts that measure following characteristics: 39 | * time, spent for `fork()` and `execve()` system calls; 40 | * time, spent for child process initialization in userspace: closing files and resetting signals -- it is time interval between finish of `fork()` call in child context and calling of `execve()`; 41 | * own program time after it was loaded with `execve()` and before it was exited. 42 | 43 | To be more correct, we should also measure time spent by `ld.so` loader and subtract it from _own program time_, but it involves complex tracing of userspace, so we leave it out of the scope of this exercise. 44 | 45 | Measure all time periods in microseconds and save them to an aggregations using process executable name and its program arguments. 46 | 47 | Use `proc_starter` experiment to demonstrate written script. This module starts `sh` shell (which can be overridden with `shell` parameter), uses `PS1` environment variable to reset prompt, and simulates real user entering commands by passing them through pseudo-terminal. Commands are represented as probability map `command`. -------------------------------------------------------------------------------- /book/kernel/ex4.md: -------------------------------------------------------------------------------- 1 | ### Exercize 4 2 | 3 | #### Part 1 4 | 5 | Implement scripts `pfstat.d` and `pfstat.stp` which will print count of pagefaults grouping by a mmapped file name (if it reachable). Print statistics once per second. Use proc\_starter experiment from [exercize 3][kernel/ex3] to demonstrate it and try to explain results you are getting (you may need to include additional outputs for that). 6 | 7 | #### Part 2 8 | 9 | Implement scripts `kmemstatp.stp` and `kmemstat.d` which will gather stats on allocations and frees on per-cache basis for a _SLAB allocator_. Use an file\_opener experiment from [exercize 1][lang/ex1] to demonstrate your script and find a correlation between number of requests per-second generated by an experiment and cache allocations. What caches are primarily used while file is opened? -------------------------------------------------------------------------------- /book/lab/iscsi.md: -------------------------------------------------------------------------------- 1 | ### iSCSI 2 | 3 | We will need to use SCSI device so we can fully trace it in [exercise 5][kernel/ex5]. Xen hypervisor supports SCSI emulation, but only by emulating outdated _LSI 53c895a_ controller which is not supported by Solaris. However, we can create iSCSI devices in Dom0 and supply them to virtual machines. The following guide is created for Debian 7 which uses _iSCSI Enterprise Target_. Recent Linux kernels replaced it with _LIO_ stack. 4 | 5 | * Install IET packages: 6 | ``` 7 | # aptitude install iscsitarget iscsitarget-dkms 8 | ``` 9 | * Create logical disks for virtual machines. They would be LVM volumes `/dev/mapper/vgmain-sol11--base--lab` and `/dev/mapper/vgmain-centos7--base--lab` in our example. 10 | * Create targets in `/etc/iet/ietd.conf` file by adding following lines: 11 | ``` 12 | Target iqn.2154-04.tdc.r520:storage.lab5-sol11-base 13 | Lun 0 Path=/dev/mapper/vgmain-sol11--base--lab,Type=blockio 14 | 15 | Target iqn.2154-04.tdc.r520:storage.lab5-centos7-base 16 | Lun 0 Path=/dev/mapper/vgmain-centos7--base--lab,Type=blockio 17 | ``` 18 | Note that target names should match DNS name of a host which provides them. 19 | * Configure `/etc/iet/initiators.allow` file to forbid Solaris access to disk allocated for CentOS machine and vice versa. Delete or comment out `ALL ALL` line and add lines with target names and IP addresses of corresponding machines: 20 | ``` 21 | iqn.2154-04.tdc.r520:storage.lab5-sol11-base 192.168.50.179 22 | iqn.2154-04.tdc.r520:storage.lab5-centos7-base 192.168.50.171 23 | ``` 24 | * Restart IET daemon: 25 | ``` 26 | # /etc/init.d/iscsitarget restart 27 | ``` 28 | * Configure Solaris initiator. `192.168.50.116` is an IP address of our Dom0 system which provides iSCSI targets. 29 | ``` 30 | # iscsiadm add discovery-address 192.168.50.116 31 | # iscsiadm modify discovery -t enable 32 | # svcadm restart svc:/network/iscsi/initiator:default 33 | ``` 34 | * Similarly configure CentOS initiator: 35 | ``` 36 | # yum install iscsi-initiator-utils 37 | # systemctl enable iscsid 38 | # systemctl start iscsid 39 | # iscsiadm -m discovery -t sendtargets -p 192.168.50.116 40 | # iscsiadm -m node --login 41 | ``` 42 | -------------------------------------------------------------------------------- /book/lang/context.md: -------------------------------------------------------------------------------- 1 | ### [__index__:context of probe] [__index__:context function] Context 2 | 3 | !!! DEF 4 | _Probe context_ contains system state related to a fired probe, including: 5 | * Register values 6 | * Thread and process, which caused probe firing, including CPU where thread is running 7 | * Currently executing probe 8 | !!! 9 | 10 | Context is provided as built-in variables in DTrace such as `execname` or as tapset functions in SystemTap such as `execname()`. 11 | 12 | Userspace register values are available in DTrace through built-in variable `uregs`. In SystemTap, they available through Embedded C and kernel function `task_pt_regs`, or a special Embedded C variable `CONTEXT`, see for example implementation of `uaddr()` and `print_regs()` tapset functions. 13 | 14 | Here are some useful context information: 15 | 16 | --- 17 | _Description_ | _DTrace_ | _SystemTap_ 18 | Current executing thread | `curthread` | `task_current()` 19 | ID of current thread | `tid` | `tid()` 20 | ID of current process | `pid` | `pid()` 21 | ID of parent of current process | `ppid` | `ppid()` 22 | User ID and group ID of current process | `uid`/`gid` | `uid()`/`gid()`, \ 23 | `euid()`, `egid()` 24 | Name of current process executable | `execname` \ 25 | `curpsinfo->ps_fname` | `execname()` 26 | Command Line Arguments | `curpsinfo->ps_psargs` | `cmdline_*()` 27 | CPU number | `cpu` | `cpu()` 28 | Probe names | `probeprov`, `probemod`, \ 29 | `probefunc`, `probename` | `pp()`, `pn()`, `ppfunc()`, \ 30 | `probefunc()`, `probemod()` 31 | --- 32 | 33 | #### References 34 | 35 | * ![image:dtraceicon](icons/dtrace.png) [Built-in Variables](http://docs.oracle.com/cd/E19253-01/817-6223/chp-variables/index.html#6mlkidlfu) 36 | * ![image:stapset](icons/stapset.png) [Context Functions](https://sourceware.org/systemtap/tapsets/context_stp.html) -------------------------------------------------------------------------------- /book/lang/ex1.md: -------------------------------------------------------------------------------- 1 | ### Exercise 1 2 | 3 | Write `opentrace.d` and `opentrace.stp` scripts which are tracing `open()` system calls. They should print following information in one line: 4 | * Call context: name of executable file, process ID, user and group IDs of user and group which are executing process. 5 | * Path to file which should be opened. 6 | * A string containing `open()` flags `O_RDONLY`, `O_WRONLY`, `O_RDWR`, `O_APPEND`, `O_CREAT` 7 | * Return value of system call 8 | 9 | For example: 10 | ``` 11 | tee[939(0:0)] open("/tmp/test", O_WRONLY|O_APPEND|O_CREAT) = 3 12 | ``` 13 | 14 | Bit flags values are presented in following table: 15 | 16 | --- 17 | __Flag__ | __Solaris__ | __Linux (x86)__ 18 | `O_RDONLY` |2,1 bits 0-1 are not set 19 | `O_WRONLY` | 1 | 1 20 | `O_RDWR` | 2 | 2 21 | `O_APPEND` | 8 | 1024 22 | `O_CREAT` | 256 |64 23 | --- 24 | 25 | Test script that your created by experimenting with redirection to file or a pipe with `tee` tool: 26 | ``` 27 | # cat /etc/inittab > /tmp/test 28 | # cat /etc/inittab >> /tmp/test 29 | # cat /etc/inittab | tee /tmp/test 30 | # cat /etc/inittab | tee -a /tmp/test 31 | ``` 32 | 33 | !!! WARN 34 | In Solaris 11 `open()` system call was replaced with more generic `openat()`. 35 | !!! 36 | 37 | _Optional_: Modify your scripts so only files that have "/etc" in their path will be shown. -------------------------------------------------------------------------------- /book/lang/ex2.md: -------------------------------------------------------------------------------- 1 | ### Exercise 2 2 | 3 | Modify scripts from Exercise 1 so they count following statistics for processes that are running in a system: 4 | * number of attempts to open existing file; 5 | * number of attempts to create a file; 6 | * number of successful attempts. 7 | 8 | At a period that is defined as command line arguments (specified in seconds) script should print: 9 | * Current time and day in human-readable format. 10 | * Table that contains gathered statistics per process along with that process name and PID. 11 | Numbers should be cleared during each iteration. 12 | 13 | You can use module `file_opener` to demonstrate your scripts. This module uses working directory which is passed as `root_dir` parameter, fills it with some files that are created preliminary (their number is set by `created_files` parameter). While executing request, it uses `file` random variable (which range is cut to `[1;max_files)`) and either tries to create a file or open it depending on `create` parameter. 14 | 15 | Run several experiments using TSLoad workload generator varying `created_files` parameter and compare the results: 16 | ``` 17 | # EXPDIR=/opt/tsload/var/tsload/file_opener 18 | # for I in 1 2 3; do 19 | mkdir /tmp/fopen$I 20 | tsexperiment -e $EXPDIR run \ 21 | -s workloads:open:params:root_dir=/tmp/fopen$I \ 22 | -s workloads:open:params:created_files=$((I * 160)) & 23 | done 24 | ``` 25 | 26 | Try to explain differences you get from the nature of `file_opener` workload generator module. -------------------------------------------------------------------------------- /book/lang/strstr.md: -------------------------------------------------------------------------------- 1 | ### [__index__:string operations] Strings 2 | 3 | Strings in dynamic tracing languages are wrappers around C-style null-terminated `char*` string, but they behave differently. In SystemTap it is simple alias, while DTrace add extra limitations, for example, you can't access single character to a string. String operations are listed in following table: 4 | 5 | --- 6 | __Operation__ | __DTrace__ | __SystemTap__ 7 | Get kernel string |1,2 `stringof ( expr )` or \ 8 | `(string) expr` | `kernel_string*()` 9 | Convert a scalar type to a string | `sprint()` and `sprintf()` 10 | Get userspace string | `copyinstr()` | `user_string*()` 11 | Compare strings |2,1 `==`, `!=`, `>`, `>=`, `<`, `<=` -- semantically equivalent to `strcmp` 12 | Concatenate two strings | `strjoin(str1, str2)` | `str1 . str2` 13 | Get string length |2,1 `strlen(str)` 14 | Check if substring is in string | `strstr(haystack, needle)` | `isinstr(haystack, needle)` 15 | --- 16 | 17 | Note that this operations may be used in DTrace predicates, for example: 18 | ``` 19 | syscall::write:entry 20 | /strstr(execname, "sh") != 0/ 21 | {} 22 | ``` 23 | 24 | #### References 25 | 26 | * ![image:dtraceicon](icons/dtrace.png) [Strings](http://docs.oracle.com/cd/E19253-01/817-6223/chp-strings/index.html) 27 | * ![image:dtraceicon](icons/dtrace.png) [Actions and Subroutines](http://docs.oracle.com/cd/E19253-01/817-6223/chp-actsub/index.html) 28 | * ![image:staplang](icons/staplang.png) [Strings](https://sourceware.org/systemtap/langref/Language_elements.html#SECTION00062300000000000000) 29 | * ![image:stapset](icons/stapset.png) [A collection of standard string functions](https://sourceware.org/systemtap/tapsets/string.stp.html) 30 | 31 | ### [__index__:structure field access] Structures 32 | 33 | Many subsystems in Linux and Solaris have to represent their data as C structures. For example, path to file corresponds from file-related structure `dentry` and filesystem-related structure `vfsmnt`: 34 | ``` 35 | struct path { 36 | struct vfsmount *mnt; 37 | struct dentry *dentry; 38 | }; 39 | ``` 40 | 41 | Structure fields are accessed same way it is done in C: in DTrace depending on what you are getting you need to use `->` for pointers and `.` for structures. In SystemTap you should always use `->` which will be contextually converted to `.` where needed. Information about structures is read from CTF sections in Solaris and DWARF sections in Linux, including field names. To get C structure you may need to cast a generic pointer (`void*` in most cases) to a needed structures. In DTrace it is done using C-style syntax: 42 | ``` 43 | (struct vnode *)((vfs_t *)this->vfsp)->vfs_vnodecovered 44 | ``` 45 | 46 | Conversion in SystemTap is used more often, because in many places, typed pointers are coerced to generic `long` type. It is performed with `@cast` expression which accepts address, name of structure as string (`struct` keyword is optional), and an optional third parameter which contains name of include file, for example: 47 | ``` 48 | function get_netdev_name:string (addr:long) { 49 | return kernel_string(@cast(addr, "net_device")->name) 50 | } 51 | ``` 52 | 53 | #### References 54 | 55 | * ![image:dtraceicon](icons/dtrace.png) [Structs and Unions](http://docs.oracle.com/cd/E19253-01/817-6223/chp-structs/index.html) 56 | * ![image:staplang](icons/staplang.png) [Expressions](https://sourceware.org/systemtap/langref/Language_elements.html#SECTION000661000000000000000) -------------------------------------------------------------------------------- /book/lang/tapset.md: -------------------------------------------------------------------------------- 1 | ### Tapsets & translators 2 | 3 | We already discussed problem with probe stability. Some issues may be related to changing data structures in kernel, or several variants may exist in kernel, for example for 32- and 64-bit calls. Let's see how access to fields of that structure may be unified. 4 | 5 | [__index__:translators (DTrace)] DTrace has a _translators_ for doing that: 6 | 7 | ````` scripts/dtrace/stat.d 8 | 9 | In this example translator describes rules of converting source structure `stat64_32` to a structure with known format defined in DTrace `stat_info`. After that, `xlate` operator is called which receives pointer to `stat64_32` structure to a `stat_info`. Note that our translator also responsible for copying data from userspace to kernel. Built-in DTrace translators are located in `/usr/lib/dtrace`. 10 | 11 | [__index__:tapset] [__index__:prologue probe alias] [__index__:epilogue probe alias] SystemTap doesn't have translators, but you can create _prologue_ or _epilogue alias_ which performs necessary conversions before (or after, respectively) probe is called. These aliases are grouped into script libraries called _tapsets_ and put into `/usr/share/systemtap/tapset` directory. Many probes that we will use in following modules are implemented in such tapsets. 12 | 13 | Linux has several variants for `stat` structure in `stat()` system call, some of them deprecated, some are intended to support 64-bit sizes for 32-bit callers. By using following tapset we will remove such differences and make them universally available through `filename` and `size` variables: 14 | 15 | ````` scripts/stap/tapset/lstat.stp 16 | 17 | This example is unrealistic: it is easier to attach to `vfs_lstat` function which has universal representation of `stat` structure and doesn't involve copying from userspace. Summarizing the syntax of creating aliases: 18 | ``` 19 | probe alias-name {=|+=} probe-name-1 [?] [,probe-name-2 [?] ...] probe-body 20 | ``` 21 | Here `=` is used for creating prologue aliases and `+=` is for epilogue aliases. Question mark `?` suffix is optional and used if some functions are not present in kernel -- it allows to choose probe from multiple possibilities. 22 | 23 | !!! WARN 24 | Note that this tapset only checks for 64-bit Intel architecture. You will need additional checks for PowerPC, AArch64 and S/390 architectures. 25 | !!! 26 | 27 | After we created this tapset, it can be used very easy: 28 | 29 | ````` scripts/stap/lstat.stp 30 | 31 | Also, sometimes we have to define constants in dynamic tracing scripts that match corresponding kernel or application constants. You can use enumerations for that in DTrace, or define a constant variable with `inline` keyword: 32 | ``` 33 | inline int TS_RUN = 2; 34 | ``` 35 | You may use initializer for global variable to do that in SystemTap: 36 | ``` 37 | global TASK_RUNNING = 0; 38 | ``` 39 | If you have enabled preprocessor with `-C` option, you may use `#define` to create macro as well. 40 | 41 | #### References 42 | 43 | * ![image:dtraceicon](icons/dtrace.png) [Translators](http://docs.oracle.com/cd/E19253-01/817-6223/chp-xlate/index.html) 44 | * ![image:staplang](icons/staplang.png) [Probe aliases](https://sourceware.org/systemtap/langref/Components_SystemTap_script.html#SECTION00042000000000000000) -------------------------------------------------------------------------------- /book/lang/time.md: -------------------------------------------------------------------------------- 1 | ### [__index__:monotonic time] [__index__:wall-clock time] Time 2 | 3 | A man used to live with a calendar and 24-hour representation of time. Coordinated Universal Time (UTC) is used for that now. These details are not needed for most kernel or application processes, so there is multiple time sources available for tracing tools: 4 | 5 | --- %60,20,20 6 | __Time source__ | __DTrace__ | __SystemTap__ 7 | _System timer_ is responsible for handling periodical events in kernel such as context switch. System timer usually ticks at constant frequency (but ticks may be omitted in _tickless kernels_). Interval between firing timer is usually referred as special unit of time: _tick_, _lbolt_ in Solaris or _jiffy_ in Linux. Timer frequency in Linux can be get using `HZ()` function. | `\`lbolt` or `\`lbolt64` | `jiffies()` 8 | _Processor cycles counter_ is a special CPU register which act as a counter which increases on each cycle, such as `TSC` in x86 or `%tick` in SPARC. It may not be monotonic. | | `get_cycles()` 9 | _Monotonic time_. Starts at unspecified moment of time (usually at system boot), but ticks with constant intervals. May use high-resolution time source such as HPET on x86, but may impose some jitter between CPU cores or CPUs. | `timestamp` | `local_clock_()` or `cpu_clock_()` 10 | _Virtual monotonic time of thread_. Similar to previous time source, but only accounts when thread is on CPU, which is useful to calculate CPU usage of a thread | `vtimestamp` | 11 | _Real time_ or _Wall-clock time_. Monotonic time source which starting point is an UNIX Epoch (00:00:00 UTC, Thursday, 1 January 1970). May use extra locks, access RTC, so it generally slower than previous time sources | `walltimestamp` | `gettimeofday_()` 12 | --- 13 | 14 | In this examples `` is one of (`s` -- seconds, `ms` -- milliseconds, `us` -- microseconds and `ns` -- nanoseconds). DTrace time sources always have nanosecond resolution. 15 | 16 | Generally speaking, monotonic time sources are better for measurement relative time intervals, while real time is used if you need precise timestamp of an event (i.e. for cross-referencing with logs). To print a real timestamp, use `ctime()` function in SystemTap which converts time to string, or use `%Y` format specifier in DTrace print functions. 17 | 18 | #### References 19 | 20 | * ![image:dtraceicon](icons/dtrace.png) [Built-in Variables](http://docs.oracle.com/cd/E19253-01/817-6223/chp-variables/index.html#6mlkidlfu) 21 | * ![image:stapset](icons/stapset.png) [Timestamp Functions](https://sourceware.org/systemtap/tapsets/timestamp_stp.html) -------------------------------------------------------------------------------- /book/principles/apply.md: -------------------------------------------------------------------------------- 1 | ### Applying tracing 2 | 3 | As we mentioned in [Tracing][tools/tracing], it is used for statistics collection and performance analysis, dynamic kernel or application debug, system audit. Imagine the situation in which various processes running by two different users are opening files: 4 | 5 | ![image:catfiles](catfiles.png) 6 | 7 | What problems can occur and how they are solved by dynamic tracing? Users can complain to very slow opening of a file, so we need to do _performance analysis_. First of all, we have confirm user complaints by measuring time spent in `open()`, `read()` and `write()` system calls. We can also try to cross-reference slow calls and filesystems on which they occur (by gathering mount paths), if problems are caused by bad NAS or disk. If the problem still exists, than you will need to go down VFS stack, i.e. by measuring time spent in block I/O or in lookup operations. 8 | 9 | If user encounters errors while opening files, then you will need to trace `errno` values. These values are usually returned by system call functions in Linux, or saved into `errno` variable in DTrace. To determine why system call returns an error, you will need _dynamically debug_ it by checking return values of callees. We will demonstrate it in following section. If users try to attempt files they do not have permissions, we can record `errno` along with paths and user ids, so by doing that we will perform _system audit_. 10 | 11 | To demonstrate it on real example, we will use following examples and run `cat /etc/shadow` from some non-root user: 12 | ``` 13 | # dtrace -qn ' 14 | syscall::open*:entry { 15 | printf("=> uid: %d pid: %d open: %s %lld\n", 16 | uid, pid, copyinstr(arg1), (long long) timestamp); 17 | } 18 | syscall::open*:return { 19 | printf("<= uid: %d pid: %d ret: %d %lld\n", 20 | uid, pid, arg1, (long long) timestamp); 21 | }' 22 | ``` 23 | 24 | SystemTap version: 25 | ``` 26 | # stap -e ' 27 | probe syscall.open { 28 | printf("=> uid: %d pid: %d open: %s %d\n", 29 | uid(), pid(), filename, local_clock_ns()); 30 | } 31 | probe syscall.open.return { 32 | printf("<= uid: %d pid: %d %d %d\n", 33 | uid(), pid(), $return, local_clock_ns()); 34 | }' 35 | ``` 36 | 37 | Here is sample output: 38 | ``` 39 | => uid: 60004 pid: 1456 open: /etc/shadow 16208212467213 40 | <= uid: 60004 pid: 1456 ret: -1 16208212482430 41 | ``` 42 | 43 | First of all, we measured time spent for `open()` system call: `16208212482430 — 16208212467213 = 15217 = 15.2 us`. We can also see that user received an error (return code is `-1`, while in case of correct call it would be positive) and now we may try to seek for a source of a problem. Finally, we have audited attempt to open critical system file `/etc/shadow` which is forbidden for users. So now we should find user name with id 60004 and politely ask him why he tried to open `/etc/shadow` file. 44 | 45 | We will discuss how trace data may be analysed and what conclusion can be made from it in this module. However, we will not introduce useful kernel or application probes as we will discuss them in modules 4, 5. On the other hand, all examples in following modules will be pure tracers, so you will need to add additional processing of results which will be discussed in this module. 46 | -------------------------------------------------------------------------------- /book/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $TITLE 9 | 10 | 11 | 12 | 13 | 24 | 25 | 34 | 35 | 36 | 37 | $HEADER 38 | 39 | 46 | 47 |
    48 |
    49 | $BODY 50 |
    51 |
    52 | 53 | $TAIL 54 | 55 | 62 | 63 | 72 | 73 | -------------------------------------------------------------------------------- /book/tools/stability.md: -------------------------------------------------------------------------------- 1 | ### Stability 2 | 3 | Another problem to which dynamic tracing systems face is stability of in-kernel interfaces. While system calls never change their interface due to backwards compatibility (if something need to be changed, new system call is introduced†), internal kernel function often do that especially if they not a public API for a drivers. Dynamic tracing languages provide mechanisms to avoid direct use of in-kernel interface by hiding them in abstractions: 4 | 5 | --- 6 | 1,2 _Stability_ |2,1 __Data access__ 7 | _DTrace_ | _SystemTap_ 8 | High | _translators_, i.e. `fileinfo_t` | tapset variables 9 | Lowest | Global variables and raw arguments like `args[0]` or `(struct_t*) arg0` | Raw arguments like `$task` or `@cast($task, "task_struct")` 10 | --- 11 | 12 | --- 13 | 1,2 _Stability_ |2,1 __Tracepoints__ 14 | _DTrace_ | _SystemTap_ 15 | High | statically defined tracing providers (like `io` and many others) | tapset aliases, i.e. `vm.kfree` 16 | Mediocre | static tracepoints with `sdt` provider | statically defined ftrace probes like `kernel.trace("kfree")` 17 | Lowest | `fbt` and `pid$$` providers | DWARF probes like `kernel.function("kfree")` 18 | --- 19 | 20 | [__index__:tapset] 21 | To achieve maximum script portability, you should pick highest stability options wherever possible. Downside of that approach is that it provides fewer information than you could access with other approaches. These options will be described in [Translators and tapsets][lang/tapset] section of next module. 22 | 23 | [__index__:conditional compilation] 24 | Linux kernel is changing faster: it has stable releases each 2-3 months, and moreover, its builds are configurable, so some features present in one kernel may be disabled in another and vice versa which makes stability is much more fragile. To overcome that, SystemTap Language has conditional compilation statements which like in C allow to disable certain paths in code. Simplest conditional compilation statements are `@defined` which evaluates to true if variable passed to it is present in debug information and `@choose_defined` which chooses from several variables. It also support ternary conditional expression: 25 | ``` 26 | %( kernel_v >= "2.6.30" 27 | %? count = kernel_long($cnt) 28 | %: count = $cnt 29 | %) 30 | ``` 31 | 32 | Here, `kernel_v` is numerical version of kernel without suffix (for version with suffix, use `kernel_vr`). SystemTap also defines `arch` variable and `CONFIG_*` tokens similiar to configuration options. These options are not available in Embedded C, use traditional preprocessor there. 33 | 34 | [__index__:missing probe] 35 | Finally, if some probe is missing from kernel, script compilation will fail. DTrace allow to ignore such errors by passing `-Z` command line option. In SystemTap you may add `?` at the end of probe name to make this probe optional. 36 | 37 | #### Notes 38 | 39 | † -- unless you are running Solaris 11 which was deprecated and obsoleted many of its system calls.. 40 | 41 | #### References 42 | 43 | * ![image:staplang](icons/staplang.png) [Conditional compilation](https://sourceware.org/systemtap/langref/Language_elements.html#SECTION00068000000000000000) 44 | * ![image:dtraceicon](icons/dtrace.png) [Stability](http://docs.oracle.com/cd/E19253-01/817-6223/chp-stab/index.html) 45 | -------------------------------------------------------------------------------- /book/tools/tracing.md: -------------------------------------------------------------------------------- 1 | ### Tracing 2 | 3 | Operating system and application are crucial parts of a computer system, but due to their colossal complexity, there are situations related to software bugs, incorrect system setup that lead to incorrect behavior. To address these issues, system administrator should perform _instrumentation_ which depends on the issue arisen: it could be performance statistics collection and their analysis, debug or system audit. Two common approaches to _instrumentation_ are _sampling_ when you collect state of the system: values of some variables, stacks of threads, etc. at unspecified moments of time and _tracing_ when you install probes at specified places of software. _Profiling_ is a most famous example of _sampling_. 4 | 5 | _Sampling_ is very helpful when you do not know where issue happens, but it hardly help when you try to know why it happened. I.e. profiling revealed that some function, say `foo()` that processes lists of elements, consumes 80% of the time, but doesn't say why: whether some lists are too long, or they should be pre-sorted, or list is inappropriate data structure for `foo()`, or whatever. With _tracing_ we can install a probe to that function, gather information on lists (say their length) and collect cumulative execution of function `foo()`, and then cross-reference them, searching for a pattern in lists whose processing costs too much CPU time. 6 | 7 | Over time operating system kernels have grown different methods of tracing. First one and a simplest one is __counters__ -- each time probe fires (say, major page fault), they increase some counter. Counters may be read through kstat interface in Solaris: 8 | ``` 9 | # kstat -p |grep maj_fault 10 | cpu:0:vm:maj_fault 7588 11 | ``` 12 | 13 | Linux usually provides counters through `procfs` or `sysfs`: 14 | ``` 15 | # cat /proc/vmstat | grep pgmajfault 16 | pgmajfault 489268 17 | ``` 18 | 19 | This approach is limited: you can't add counter for every event without losing performance, and they are usually system-wide (i.e. you can't know what process causing major-faults), or process/thread-wide. 20 | 21 | More complex approach is __debug printing__: add a `printk()` or `cmn_err()` statement as a probe, but this approach is quite limited, because you need recompile kernel each time you need new set of probes. But if all debug printing will be enabled, you will get excessive system load. By default, most of debug printing in Solaris are disabled unless you compile a DEBUG-build, which is not publicly available. Modern Linux kernels however developed a dynamic debugging facility available via `pr_debug()`. There are several __static probes__ which are deactivated on systems start, but can be activated externally: _ftrace_ and _kprobes_ in Linux and _TNF_ on Solaris, but amount of information provided by them is still limited, and _ftrace_/_kprobes_ are requiring writing kernel modules which is not convenient and dangerous. 22 | 23 | So, generally speaking, that approaches provide very limited set of data at very limited set of tracing points. The only approach that widens that limits is __kernel debugger__, but because each breakpoint halts system, __it cannot be used on production systems__. The answer to them are __dynamic tracing__ which is the topic of this book. 24 | -------------------------------------------------------------------------------- /book/watermark.txt: -------------------------------------------------------------------------------- 1 | This cheatsheet is based on 2 | Dynamic Tracing with DTrace & SystemTap 3 | book by Sergey Klyaus
    4 | This work is licensed under the (CC BY-NC-SA 3.0) License. -------------------------------------------------------------------------------- /experiments/concurrency/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "concurrency", 3 | "steps": { 4 | "worker": { 5 | "num_steps": 20, 6 | "num_requests": 40, 7 | } 8 | }, 9 | "threadpools": { 10 | "tp_worker": { 11 | "num_threads": 2, 12 | "quantum": 1000000000, 13 | "disp": { 14 | "type": "benchmark" 15 | }, 16 | "sched" : [ 17 | { "wid": "all", 18 | "objects": ["strand:0:0:0"] } 19 | ] 20 | } 21 | }, 22 | "workloads" : { 23 | "worker": { 24 | "wltype": "busy_wait", 25 | "threadpool": "tp_worker", 26 | "rqsched": { "type": "simple" }, 27 | "params": { "num_cycles": 4000000 } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /experiments/deblock/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deblock", 3 | "steps": { 4 | "fileio": { 5 | "num_steps": 20, 6 | "num_requests": 80 7 | } 8 | }, 9 | "threadpools": { 10 | "tp_fileio": { 11 | "num_threads": 4, 12 | "quantum": 1000000000, 13 | "disp": { "type": "round-robin" } 14 | } 15 | }, 16 | "workloads": { 17 | "fileio": { 18 | "wltype": "fileio", 19 | "threadpool": "tp_fileio", 20 | "rqsched": { "type": "iat", "distribution": "exponential" }, 21 | "params": { 22 | "path": "/tiger/DEBLOCK", 23 | "file_size": 1048576, 24 | "sync": true, 25 | "rw": "write", 26 | "block_size": { 27 | "randgen": { "class": "lcg" }, 28 | "randvar": { "class": "uniform", 29 | "min": 512, 30 | "max": 16384 } 31 | }, 32 | "offset": { "randgen": { "class": "lcg" } } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /experiments/drupal/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drupal", 3 | "steps": { 4 | "drupal": { 5 | "num_steps": 10, 6 | "num_requests": 14 7 | } 8 | }, 9 | "threadpools": { 10 | "drupal_users" : { 11 | "num_threads": 8, 12 | "quantum": 4000000000, 13 | "disp": { 14 | "type": "user" 15 | } 16 | } 17 | }, 18 | "workloads" : { 19 | "drupal" : { 20 | "wltype": "http", 21 | "threadpool": "drupal_users", 22 | "rqsched": { 23 | "type": "think", 24 | "nusers": 8, 25 | "distribution": "exponential" 26 | }, 27 | "params": { 28 | "server": "localhost", 29 | "port": 80, 30 | "uri": { 31 | "randgen": { "class" : "lcg" }, 32 | "pmap" : [ 33 | { 34 | "value": "/drupal/", 35 | "probability": 0.4 36 | }, 37 | { 38 | "value": "/drupal/?q=node&page=1", 39 | "probability": 0.3 40 | }, 41 | { 42 | "valarray": 43 | [ "/drupal/?q=node/1", 44 | "/drupal/?q=node/2", 45 | "/drupal/?q=node/3", 46 | "/drupal/?q=node/4", 47 | "/drupal/?q=node/5", 48 | "/drupal/?q=node/6", 49 | "/drupal/?q=node/7", 50 | "/drupal/?q=node/8", 51 | "/drupal/?q=node/9", 52 | "/drupal/?q=node/10", 53 | "/drupal/?q=node/11", 54 | "/drupal/?q=node/12", 55 | "/drupal/?q=node/13", 56 | "/drupal/?q=node/14", 57 | "/drupal/?q=node/15", 58 | "/drupal/?q=node/16", 59 | "/drupal/?q=node/17", 60 | "/drupal/?q=node/18", 61 | "/drupal/?q=node/19", 62 | "/drupal/?q=node/20", 63 | "/drupal/?q=node/21", 64 | "/drupal/?q=node/22", 65 | "/drupal/?q=node/23", 66 | "/drupal/?q=node/24", 67 | "/drupal/?q=node/25", 68 | "/drupal/?q=node/26", 69 | "/drupal/?q=node/27", 70 | "/drupal/?q=node/28", 71 | "/drupal/?q=node/29", 72 | "/drupal/?q=node/30", 73 | "/drupal/?q=node/31", 74 | "/drupal/?q=node/32", 75 | "/drupal/?q=node/33", 76 | "/drupal/?q=node/34", 77 | "/drupal/?q=node/35", 78 | "/drupal/?q=node/36", 79 | "/drupal/?q=node/37", 80 | "/drupal/?q=node/38", 81 | "/drupal/?q=node/39", 82 | "/drupal/?q=node/40", 83 | "/drupal/?q=node/41", 84 | "/drupal/?q=node/42", 85 | "/drupal/?q=node/43", 86 | "/drupal/?q=node/44", 87 | "/drupal/?q=node/45", 88 | "/drupal/?q=node/46", 89 | "/drupal/?q=node/47", 90 | "/drupal/?q=node/48", 91 | "/drupal/?q=node/49", 92 | "/drupal/?q=node/50" 93 | ], 94 | "probability": 0.3 95 | } 96 | ] 97 | } 98 | } 99 | }, 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /experiments/duality/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "duality", 3 | "steps": { 4 | "manager": { 5 | "num_steps": 20, 6 | "num_requests": 20, 7 | }, 8 | "worker": { 9 | "num_steps": 20, 10 | "num_requests": 20, 11 | } 12 | }, 13 | "threadpools": { 14 | "tp_manager": { 15 | "num_threads": 1, 16 | "quantum": 1000000000, 17 | "disp": { 18 | "type": "round-robin" 19 | }, 20 | "sched" : [ 21 | { "wid": 0, 22 | "objects": ["strand:0:0:0"] } 23 | ] 24 | }, 25 | "tp_worker": { 26 | "num_threads": 1, 27 | "quantum": 1000000000, 28 | "disp": { 29 | "type": "benchmark" 30 | }, 31 | "sched" : [ 32 | { "wid": 0, 33 | "objects": ["strand:0:0:0"] } 34 | ] 35 | } 36 | }, 37 | "workloads" : { 38 | "manager": { 39 | "wltype": "busy_wait", 40 | "threadpool": "tp_manager", 41 | "rqsched": { 42 | "type": "iat", 43 | "distribution": "exponential" 44 | }, 45 | "params": { "num_cycles": 800000 } 46 | }, 47 | "worker": { 48 | "wltype": "busy_wait", 49 | "threadpool": "tp_worker", 50 | "rqsched": { "type": "simple" }, 51 | "params": { "num_cycles": 4000000 } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /experiments/pthread/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pthread", 3 | "steps": { 4 | "busy": { 5 | "num_steps": 20, 6 | "num_requests": 1000 7 | } 8 | }, 9 | "threadpools": { 10 | "busy_tp" : { 11 | "num_threads": 2, 12 | "quantum": 1000000000, 13 | "disp": { 14 | "type": "round-robin" 15 | } 16 | } 17 | }, 18 | "workloads" : { 19 | "busy" : { 20 | "wltype": "busy_wait", 21 | "threadpool": "busy_tp", 22 | "rqsched": { 23 | "type": "iat", 24 | "distribution": "exponential" 25 | }, 26 | "params": { 27 | "num_cycles": 100000 28 | } 29 | }, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /experiments/readahead/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "readahead", 3 | "steps": { 4 | "fileio": { 5 | "num_steps": 20, 6 | "num_requests": 80 7 | } 8 | }, 9 | "threadpools": { 10 | "tp_fileio": { 11 | "num_threads": 4, 12 | "quantum": 1000000000, 13 | "disp": { "type": "round-robin" } 14 | } 15 | }, 16 | "workloads": { 17 | "fileio": { 18 | "wltype": "fileio", 19 | "threadpool": "tp_fileio", 20 | "rqsched": { "type": "iat", "distribution": "exponential" }, 21 | "params": { 22 | "path": "/tiger/READAHEAD", 23 | "file_size": 20971520, 24 | "overwrite": true, 25 | "sync": false, 26 | "rw": "read", 27 | "block_size": 512, 28 | "offset": { "randgen": { "class": "seq" } } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /fonts/DejaVuSansMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/fonts/DejaVuSansMono-Bold.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansMono-BoldOblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/fonts/DejaVuSansMono-BoldOblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansMono-Oblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/fonts/DejaVuSansMono-Oblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/fonts/DejaVuSansMono.ttf -------------------------------------------------------------------------------- /fonts/lcmss8.pfb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/fonts/lcmss8.pfb -------------------------------------------------------------------------------- /images/SConscript: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from itertools import chain 5 | 6 | Import('env') 7 | 8 | def _inkscape_builder(opt, suffix): 9 | return Builder(action = Action('inkscape -z %s $TARGET $SOURCE' % opt), 10 | suffix = suffix, src_suffix = '.svg') 11 | 12 | InkscapeBuilder = _inkscape_builder('-e', '.svg.png') 13 | InkscapeSVGBuilder = _inkscape_builder('-l', '.plain.svg') 14 | InkscapePDFBuilder = _inkscape_builder('-A', '.pdf') 15 | 16 | CompressBuilder = Builder(action = Action('convert -define png:compression-level=9 -define png:compression-strategy=0 -define png:compression-filter=0 $SOURCE $TARGET'), 17 | suffix = '.png', 18 | src_suffix = '.svg.png') 19 | SVGMerger = Builder(action = Action('%s images/merge-svgs.py $TARGET $SOURCES' % (sys.executable)), 20 | src_suffix = '.svg', suffix = '.svg') 21 | 22 | 23 | env.Append(BUILDERS = {'InkscapeBuilder': InkscapeBuilder, 24 | 'InkscapeSVGBuilder': InkscapeSVGBuilder, 25 | 'InkscapePDFBuilder': InkscapePDFBuilder, 26 | 'SVGMerger': SVGMerger, 27 | 'CompressBuilder': CompressBuilder}) 28 | 29 | 30 | def ConvertSVGs(env, imgdir, width): 31 | img = env.Clone() 32 | img['PNGWIDTH'] = width 33 | 34 | tgt = [] 35 | 36 | for image in imgdir.glob('*.svg'): 37 | if image.abspath.endswith('plain.svg'): 38 | continue 39 | 40 | img.CompressBuilder(None, img.InkscapeBuilder(None, image)) 41 | img.InkscapeSVGBuilder(None, image) 42 | img.InkscapePDFBuilder(None, image) 43 | 44 | for image in chain(imgdir.glob('*.png'), imgdir.glob('*.plain.svg'), imgdir.glob('*.pdf')): 45 | env.Depends('images', tgt) 46 | 47 | env.SVGMerger('netprobes2.svg', ['netprobes.svg', 'net.svg']) 48 | 49 | ConvertSVGs(env, Dir('.'), 800) 50 | ConvertSVGs(env, Dir('linux'), 800) 51 | ConvertSVGs(env, Dir('solaris'), 800) 52 | ConvertSVGs(env, Dir('conv'), 300) 53 | ConvertSVGs(env, Dir('icons'), 24) -------------------------------------------------------------------------------- /images/density.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/images/density.png -------------------------------------------------------------------------------- /images/heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/images/heatmap.png -------------------------------------------------------------------------------- /images/linear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/images/linear.png -------------------------------------------------------------------------------- /images/merge-svgs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import xml.etree.ElementTree as etree 5 | 6 | NAMESPACES = { 7 | '': 'http://www.w3.org/2000/svg', 8 | 'xlink': 'http://www.w3.org/1999/xlink', 9 | 'sodipodi': 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', 10 | 'inkscape': 'http://www.inkscape.org/namespaces/inkscape' 11 | } 12 | 13 | def _xml_tag(tag, ns=''): 14 | return '{{{0}}}{1}'.format(NAMESPACES[ns], tag) 15 | 16 | # Parse args and process 17 | target = sys.argv[1] 18 | source = sys.argv[2] 19 | extrasvgs = dict((os.path.basename(fname), fname) 20 | for fname in sys.argv[3:]) 21 | 22 | for ns, uri in NAMESPACES.items(): 23 | etree.register_namespace(ns, uri) 24 | 25 | # Parse main image 26 | tree = etree.parse(source) 27 | root = tree.getroot() 28 | 29 | for group in root.findall('.//' + _xml_tag('g')): 30 | use = group.find(_xml_tag('use')) 31 | if use is None: 32 | continue 33 | 34 | href = use.attrib[_xml_tag('href', 'xlink')] 35 | 36 | if href not in extrasvgs: 37 | raise ValueError('Invalid use reference {0}'.format(href)) 38 | 39 | group.clear() 40 | 41 | # Read sub svg 42 | extrasvg = etree.parse(extrasvgs[href]).getroot() 43 | extragroup = extrasvg.find(_xml_tag('g')) 44 | if extragroup is not None: 45 | group.extend(extragroup) 46 | 47 | tree.write(target) -------------------------------------------------------------------------------- /images/oncpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myaut/dtrace-stap-book/92f30d688f4caec1c17e702ec1e886687a0111f1/images/oncpu.png -------------------------------------------------------------------------------- /scripts/dtrace/callgraph.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -s 2 | 3 | #pragma D option flowindent 4 | 5 | syscall::open*:entry 6 | /pid == $target && copyinstr(arg0) == "not_exists"/ 7 | { 8 | self->traceme = 1; 9 | } 10 | 11 | syscall::open*:return 12 | /self->traceme/ 13 | { 14 | self->traceme = 0; 15 | } 16 | 17 | fbt:::entry 18 | /self->traceme && probefunc != "bcmp"/ 19 | { 20 | 21 | } 22 | 23 | fbt:::return 24 | /self->traceme && probefunc != "bcmp"/ 25 | { 26 | trace(arg1); 27 | } 28 | -------------------------------------------------------------------------------- /scripts/dtrace/cvtrace.d: -------------------------------------------------------------------------------- 1 | cv_wait*:entry { 2 | self->timeout = 0; 3 | } 4 | 5 | cv_timedwait_hires:entry, 6 | cv_timedwait_sig_hires:entry { 7 | self->timeout = (arg2 / arg3) * arg3; 8 | } 9 | 10 | cv_wait:entry, 11 | cv_wait_sig:entry, 12 | cv_wait_sig_swap_core:entry, 13 | cv_timedwait_hires:entry, 14 | cv_timedwait_sig_hires:entry { 15 | printf("[%d] %s %s cv: %p mutex: %p timeout: %d\n", 16 | pid, execname, probefunc, arg0, arg1, self->timeout); 17 | stack(4); 18 | } 19 | 20 | cv_signal:entry, 21 | cv_broadcast:entry { 22 | printf("[%d] %s %s cv: %p\n", 23 | pid, execname, probefunc, arg0); 24 | stack(4); 25 | } 26 | -------------------------------------------------------------------------------- /scripts/dtrace/deblock.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qCs 2 | 3 | #define VFSMNTPT(vfs) ((vfs)->vfs_vnodecovered \ 4 | ? stringof((vfs)->vfs_vnodecovered->v_path) \ 5 | : "???") 6 | #define NBITSMINOR 32 7 | #define MAXMIN 0xFFFFFFFF 8 | 9 | fbt::fop_write:entry 10 | /args[1]->uio_resid != 0/ { 11 | this->dev = args[0]->v_vfsp->vfs_dev; 12 | @vfs[getmajor(this->dev), 13 | getminor(this->dev), 14 | VFSMNTPT(args[0]->v_vfsp)] = sum(args[1]->uio_resid); 15 | } 16 | 17 | io:::start 18 | /args[0]->b_bcount != 0 && args[0]->b_flags & B_WRITE/ { 19 | @bio[args[1]->dev_major, 20 | args[1]->dev_minor, 21 | args[1]->dev_statname] = sum(args[0]->b_bcount); 22 | } 23 | 24 | tick-1s { 25 | normalize(@vfs, 1024); normalize(@bio, 1024); 26 | 27 | printf("%9s %16s %8s BDEV KB/s\n", "DEV_T", "NAME", "VFS KB/s"); 28 | printa("%3d,%-5d %16s %8@u %@u\n", @vfs, @bio); 29 | 30 | trunc(@vfs); trunc(@bio); 31 | } -------------------------------------------------------------------------------- /scripts/dtrace/forktime.d: -------------------------------------------------------------------------------- 1 | uint64_t tm_fork_start[int]; 2 | uint64_t tm_fork_end[int]; 3 | uint64_t tm_exec_start[int]; 4 | uint64_t tm_exec_end[int]; 5 | 6 | syscall::*fork*:entry { 7 | self->tm_fork_start = timestamp; 8 | } 9 | syscall::*fork*:return 10 | /arg1 > 0/ 11 | { 12 | tm_fork_start[arg1] = self->tm_fork_start; 13 | } 14 | 15 | proc:::start { 16 | tm_fork_end[pid] = timestamp; 17 | } 18 | proc:::exec { 19 | tm_exec_start[pid] = timestamp; 20 | } 21 | proc:::exec-* { 22 | tm_exec_end[pid] = timestamp; 23 | } 24 | 25 | proc:::exit 26 | / tm_fork_start[pid] > 0 && tm_fork_end[pid] > 0 && 27 | tm_exec_start[pid] > 0 && tm_exec_end[pid] > 0 / 28 | { 29 | @fork[curpsinfo->pr_fname, curpsinfo->pr_psargs] = 30 | avg(tm_fork_end[pid] - tm_fork_start[pid]); 31 | @postfork[curpsinfo->pr_fname, curpsinfo->pr_psargs] = 32 | avg(tm_exec_start[pid] - tm_fork_end[pid]); 33 | @exec[curpsinfo->pr_fname, curpsinfo->pr_psargs] = 34 | avg(tm_exec_end[pid] - tm_exec_start[pid]); 35 | @proc[curpsinfo->pr_fname, curpsinfo->pr_psargs] = 36 | avg(timestamp - tm_exec_end[pid]); 37 | 38 | tm_fork_start[pid] = 0; tm_fork_end[pid] = 0; 39 | tm_exec_start[pid] = 0; tm_exec_end[pid] = 0; 40 | } 41 | 42 | tick-1s { 43 | normalize(@fork, 1000); normalize(@postfork, 1000); 44 | normalize(@exec, 1000); normalize(@proc, 1000); 45 | 46 | printf("%32s %8s %8s %8s %8s\n", 47 | "COMMAND", "FORK", "POSTFORK", "EXEC", "PROC"); 48 | printa("%10s %22s %@6dus %@6dus %@6dus %@6dus\n", 49 | @fork, @exec, @postfork, @proc); 50 | 51 | clear(@fork); clear(@postfork); clear(@exec); clear(@proc); 52 | } 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /scripts/dtrace/hotspot.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qs 2 | 3 | #pragma D option switchrate=10hz 4 | 5 | hotspot$target:::class-loaded 6 | { 7 | printf("%12s [???] %s\n", probename, stringof(copyin(arg0, arg1))); 8 | } 9 | 10 | hotspot$target:::method-entry, 11 | hotspot$target:::method-return 12 | { 13 | printf("%12s [%3d] %s.%s\n", probename, arg0, 14 | stringof(copyin(arg1, arg2)), 15 | stringof(copyin(arg3, arg4))); 16 | } 17 | 18 | hotspot$target:::thread-start, 19 | hotspot$target:::thread-stop 20 | { 21 | printf("%12s [%3d] %s\n", probename, arg3, 22 | stringof(copyin(arg0, arg1))); 23 | } 24 | 25 | hotspot$target:::monitor-contended-enter, 26 | hotspot$target:::monitor-contended-exit 27 | { 28 | printf("%12s [%3d] %s\n", probename, arg0, 29 | stringof(copyin(arg2, arg3))); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /scripts/dtrace/kmemstat.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qCs 2 | 3 | #define CACHE_NAME(arg) ((kmem_cache_t*) arg)->cache_name 4 | 5 | fbt::kmem_cache_alloc:entry { 6 | @allocs[CACHE_NAME(arg0)] = count(); 7 | } 8 | 9 | fbt::kmem_cache_free:entry { 10 | @frees[CACHE_NAME(arg0)] = count(); 11 | } 12 | 13 | tick-1s { 14 | printf("%8s %8s %s\n", "ALLOCS", "FREES", "SLAB"); 15 | printa("%@8u %@8u %s\n", @allocs, @frees); 16 | 17 | trunc(@allocs); trunc(@frees); 18 | } -------------------------------------------------------------------------------- /scripts/dtrace/mtxtime.d: -------------------------------------------------------------------------------- 1 | pid$target::mutex_lock_impl:entry 2 | { 3 | self->mtxtime = timestamp; 4 | } 5 | 6 | plockstat$target:::mutex-acquire 7 | / self->mtxtime != 0 / 8 | { 9 | @[ustack()] = quantize(timestamp - self->mtxtime); 10 | self->mtxtime = 0; 11 | } 12 | 13 | pid$target::experiment_unconfigure:entry 14 | { 15 | printa(@); 16 | } -------------------------------------------------------------------------------- /scripts/dtrace/openaggr.d: -------------------------------------------------------------------------------- 1 | syscall::openat*:entry 2 | /(arg2 & O_CREAT) == O_CREAT/ { 3 | @create[execname, pid] = count(); 4 | } 5 | 6 | syscall::openat*:entry 7 | /(arg2 & O_CREAT) == 0/ { 8 | @open[execname, pid] = count(); 9 | } 10 | 11 | syscall::openat*:return 12 | / arg1 > 0 / { 13 | @success[execname, pid] = count(); 14 | } 15 | 16 | tick-$1s { 17 | printf("%Y\n", walltimestamp); 18 | printf("%12s %6s %6s %6s %s\n", 19 | "EXECNAME", "PID", "CREATE", "OPEN", "SUCCESS"); 20 | printa("%12s %6d %@6d %@6d %@d\n", @create, @open, @success); 21 | trunc(@create); trunc(@open); trunc(@success); 22 | } -------------------------------------------------------------------------------- /scripts/dtrace/opentrace.d: -------------------------------------------------------------------------------- 1 | /* These constants are already defined in /usr/lib/dtrace/io.d 2 | inline int O_WRONLY = 1; 3 | inline int O_RDWR = 2; 4 | inline int O_APPEND = 8; 5 | inline int O_CREAT = 256; 6 | */ 7 | 8 | this string flag_str; 9 | 10 | syscall::openat*:entry { 11 | self->path = copyinstr(arg1); 12 | self->flags = arg2; 13 | } 14 | 15 | syscall::openat*:return 16 | { 17 | this->flags_str = strjoin( 18 | self->flags & O_WRONLY 19 | ? "O_WRONLY" 20 | : self->flags & O_RDWR 21 | ? "O_RDWR" 22 | : "O_RDONLY", 23 | strjoin( 24 | self->flags & O_APPEND ? "|O_APPEND" : "", 25 | self->flags & O_CREAT ? "|O_CREAT" : "")); 26 | 27 | printf("%s[%d(%d:%d)] open(\"%s\", %s) = %d\n", 28 | execname, pid, uid, gid, 29 | self->path, this->flags_str, arg1); 30 | } -------------------------------------------------------------------------------- /scripts/dtrace/pagefault.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qCs 2 | 3 | /** 4 | * pagefault.d 5 | * 6 | * Traces page faults which are handled by as_fault() 7 | * 8 | * Tested on Solaris 11 9 | */ 10 | 11 | string fault_type[4]; 12 | string seg_rw_type[6]; 13 | string prot[8]; 14 | 15 | #define DUMP_AS_FAULT() \ 16 | printf("as_fault pid: %d as: %p\n", pid, self->as); \ 17 | printf("\taddr: %p size: %d flags: %s|%s \n", \ 18 | self->addr, self->size, \ 19 | fault_type[self->pf_type], \ 20 | seg_rw_type[self->pf_rw] \ 21 | ) 22 | 23 | #define PROT(p) prot[(p) & 0x7], \ 24 | ((p) & 0x8) ? "u" : "-" 25 | 26 | #define VNODE_NAME(vp) (vp) \ 27 | ? ((vp)->v_path) \ 28 | ? stringof((vp)->v_path) \ 29 | : "???" \ 30 | : "[ anon ]" 31 | 32 | #define DUMP_SEG_VN(seg, seg_vn) \ 33 | printf("\t[%p:%p] %s%s\n\tvn: %s\n\tamp: %p:%d \n", \ 34 | (seg)->s_base, (seg)->s_base + (seg)->s_size, \ 35 | PROT((seg_vn)->prot), VNODE_NAME((seg_vn)->vp), \ 36 | (seg_vn)->amp, (seg_vn)->anon_index \ 37 | ) 38 | 39 | #define IS_SEG_VN(s) (((struct seg*) s)->s_ops == &`segvn_ops) 40 | 41 | BEGIN { 42 | /* See vm/seg_enum.h */ 43 | fault_type[0] = "F_INVAL"; fault_type[1] = "F_PROT"; 44 | fault_type[2] = "F_SOFTLOCK"; fault_type[3] = "F_SOFTUNLOCK"; 45 | 46 | seg_rw_type[0] = "S_OTHER"; seg_rw_type[1] = "S_READ"; 47 | seg_rw_type[2] = "S_WRITE"; seg_rw_type[3] = "S_EXEC"; 48 | seg_rw_type[4] = "S_CREATE"; seg_rw_type[5] = "S_READ_NOCOW"; 49 | 50 | prot[0] = "---"; prot[1] = "r--"; 51 | prot[2] = "-w-"; prot[3] = "rw-"; 52 | prot[4] = "--x"; prot[5] = "r-x"; 53 | prot[6] = "-wx"; prot[7] = "rwx"; 54 | } 55 | 56 | fbt::as_fault:entry { 57 | self->in_fault = 1; 58 | 59 | self->as = args[1]; 60 | self->addr = args[2]; 61 | self->size = args[3]; 62 | 63 | self->pf_type = args[4]; 64 | self->pf_rw = args[5]; 65 | } 66 | 67 | fbt::as_fault:return 68 | { 69 | self->in_fault = 0; 70 | } 71 | 72 | fbt::as_segat:return 73 | /self->in_fault && arg1 == 0/ 74 | { 75 | DUMP_AS_FAULT(); 76 | } 77 | 78 | fbt::as_segat:return 79 | /self->in_fault && arg1 != 0 && IS_SEG_VN(arg1)/ 80 | { 81 | this->seg = (struct seg*) arg1; 82 | this->seg_vn = (segvn_data_t*) this->seg->s_data; 83 | 84 | DUMP_AS_FAULT(); 85 | DUMP_SEG_VN(this->seg, this->seg_vn); 86 | } 87 | -------------------------------------------------------------------------------- /scripts/dtrace/pfstat.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qCs 2 | 3 | #define VNODE_NAME(vp) \ 4 | (vp) ? ((vp)->v_path) \ 5 | ? stringof((vp)->v_path) : "???" : "[ anon ]" 6 | 7 | #define IS_SEG_VN(s) (((struct seg*) s)->s_ops == &`segvn_ops) 8 | 9 | fbt::as_fault:entry { 10 | self->in_fault = 1; 11 | } 12 | fbt::as_fault:return { 13 | self->in_fault = 0; 14 | } 15 | 16 | fbt::as_segat:return 17 | /self->in_fault && arg1 == 0/ { 18 | @faults["???"] = count(); 19 | } 20 | 21 | fbt::as_segat:return 22 | /self->in_fault && arg1 != 0 && IS_SEG_VN(arg1)/ { 23 | this->seg = (struct seg*) arg1; 24 | this->seg_vn = (segvn_data_t*) this->seg->s_data; 25 | 26 | @faults[VNODE_NAME(this->seg_vn->vp)] = count(); 27 | } 28 | 29 | tick-1s { 30 | printf("%8s %s\n", "FAULTS", "VNODE"); 31 | printa("%@8u %s\n", @faults); 32 | trunc(@faults); 33 | } -------------------------------------------------------------------------------- /scripts/dtrace/proc.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qCs 2 | 3 | #define PARENT_EXECNAME(thread) \ 4 | (thread->t_procp->p_parent != NULL) \ 5 | ? stringof(thread->t_procp->p_parent->p_user.u_comm) \ 6 | : "???" 7 | 8 | proc:::, syscall::fork*:entry, syscall::exec*:entry, 9 | syscall::wait*:entry { 10 | printf("%6d[%8s]/%6d[%8s] %s::%s:%s\n", 11 | pid, execname, ppid, PARENT_EXECNAME(curthread), 12 | probeprov, probefunc, probename); 13 | } 14 | 15 | proc:::create { 16 | printf("\tPID: %d -> %d\n", curpsinfo->pr_pid, args[0]->pr_pid); 17 | } 18 | 19 | 20 | proc:::exec { 21 | printf("\tfilename: %s\n", args[0]); 22 | } 23 | 24 | proc:::exit { 25 | printf("\treturn code: %d\n", args[0]); 26 | } 27 | -------------------------------------------------------------------------------- /scripts/dtrace/pthread.d: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dtrace 2 | #pragma D option bufsize=8m 3 | #pragma D option switchrate=100hz 4 | 5 | pid$target::pthread_create:entry { 6 | self->thread_id_ptr = (uintptr_t) arg0; 7 | self->thread_func = arg2; 8 | self->thread_arg = arg3; 9 | } 10 | pid$target::pthread_create:return { 11 | this->thread_id = * (uint_t*) copyin(self->thread_id_ptr, sizeof(uint_t)); 12 | printf("[%d] pthread_create %x ", tid, this->thread_id); 13 | usym(self->thread_func); 14 | printf("(%x)\n", self->thread_arg); 15 | } 16 | pid$target::pthread_join:entry { 17 | self->thread_id = (uint_t) arg0; 18 | printf("[%d] pthread_join %x\n", tid, self->thread_id); 19 | } 20 | pid$target::pthread_join:return { 21 | printf("[%d] pthread_join:return %x -> %d\n", tid, self->thread_id, arg1); 22 | } 23 | 24 | plockstat$target:::, 25 | pid$target::pthread_cond_*wait*:entry, 26 | pid$target::pthread_cond_*wait*:return, 27 | pid$target::pthread_cond_signal:entry, 28 | pid$target::pthread_cond_broadcast:entry { 29 | printf("[%d] %s:%s ", tid, probefunc, probename); 30 | usym(arg0); 31 | printf("[%p]\n", arg0); 32 | ustack(6); 33 | } -------------------------------------------------------------------------------- /scripts/dtrace/pycode.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -Cs 2 | 3 | #include "pycode.h" 4 | 5 | #define GET_Py3_STRING(obj) (((PyUnicodeObject*) copyin((uintptr_t) obj, \ 6 | sizeof(PyUnicodeObject)))->flags[0] & 0xE0) \ 7 | ? copyinstr(((uintptr_t) obj) + sizeof(PyUnicodeObject)) : "" 8 | 9 | foo$target::: {} 10 | 11 | pid$target::PyEval_EvalCodeEx:entry { 12 | self->co = (PyCodeObject*) copyin(arg0, sizeof(PyCodeObject)); 13 | 14 | trace(GET_Py3_STRING(self->co->co_filename)); 15 | trace(GET_Py3_STRING(self->co->co_name)); 16 | trace(self->co->co_firstlineno); 17 | } -------------------------------------------------------------------------------- /scripts/dtrace/pycode.h: -------------------------------------------------------------------------------- 1 | #ifndef PY_CODE_H 2 | #define PY_CODE_H 3 | 4 | /** 5 | * This is forward definitions taken from Include/object.h and Include/code.h 6 | * to support extraction of Python 3.4 interpreter state 7 | */ 8 | 9 | typedef long ssize_t; 10 | 11 | typedef struct _object { 12 | /* _PyObject_HEAD_EXTRA */ 13 | ssize_t ob_refcnt; 14 | struct PyObject *ob_type; 15 | } PyObject; 16 | 17 | /* Bytecode object */ 18 | typedef struct _code { 19 | PyObject base; 20 | int co_argcount; /* #arguments, except *args */ 21 | int co_kwonlyargcount; /* #keyword only arguments */ 22 | int co_nlocals; /* #local variables */ 23 | int co_stacksize; /* #entries needed for evaluation stack */ 24 | int co_flags; /* CO_..., see below */ 25 | PyObject *co_code; /* instruction opcodes */ 26 | PyObject *co_consts; /* list (constants used) */ 27 | PyObject *co_names; /* list of strings (names used) */ 28 | PyObject *co_varnames; /* tuple of strings (local variable names) */ 29 | PyObject *co_freevars; /* tuple of strings (free variable names) */ 30 | PyObject *co_cellvars; /* tuple of strings (cell variable names) */ 31 | /* The rest doesn't count for hash or comparisons */ 32 | unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */ 33 | PyObject *co_filename; /* unicode (where it was loaded from) */ 34 | PyObject *co_name; /* unicode (name, for reference) */ 35 | int co_firstlineno; /* first source line number */ 36 | PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See 37 | Objects/lnotab_notes.txt for details. */ 38 | void *co_zombieframe; /* for optimization only (see frameobject.c) */ 39 | PyObject *co_weakreflist; /* to support weakrefs to code objects */ 40 | } PyCodeObject; 41 | 42 | /** 43 | * Compact ASCII object from Python3 -- data starts after PyUnicodeObject -- only if compact, ascii 44 | * and ready flags are set 45 | */ 46 | typedef struct _unicode { 47 | PyObject base; 48 | ssize_t length; 49 | ssize_t hash; 50 | char flags[4]; 51 | void* wstr; 52 | } PyUnicodeObject; 53 | 54 | #endif -------------------------------------------------------------------------------- /scripts/dtrace/pymalloc.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qCZs 2 | 3 | BEGIN { 4 | self->depth = 0; 5 | } 6 | 7 | foo$target::: { 8 | /* This probe is just a workaround for -xlazyload */ 9 | } 10 | 11 | python$target:::function-entry { 12 | func_stack[self->depth] = arg1; 13 | file_stack[self->depth] = arg0; 14 | 15 | self->depth++; 16 | } 17 | python$target:::function-return { 18 | self->depth--; 19 | } 20 | 21 | pid$target::malloc:entry 22 | / func_stack[self->depth] != 0 / { 23 | @mallocs[copyinstr(func_stack[self->depth]), 24 | copyinstr(file_stack[self->depth])] = sum(arg0); 25 | } -------------------------------------------------------------------------------- /scripts/dtrace/readahead.d: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/dtrace -qCs 2 | 3 | #define VFSMNTPT(vfs) ((vfs)->vfs_vnodecovered \ 4 | ? stringof((vfs)->vfs_vnodecovered->v_path) \ 5 | : "???") 6 | #define HASDI(bp) (((struct buf*) bp)->b_dip != 0) 7 | #define DEVINFO(bp) xlate((struct buf*) bp) 8 | 9 | fbt::fop_read:entry 10 | /args[1]->uio_resid != 0/ { 11 | this->dev = args[0]->v_vfsp->vfs_dev; 12 | @vfs[getmajor(this->dev), 13 | getminor(this->dev), 14 | VFSMNTPT(args[0]->v_vfsp)] = count(); 15 | } 16 | 17 | io:::start 18 | /args[0]->b_bcount != 0 && args[0]->b_flags & B_READ/ { 19 | @bio[args[1]->dev_major, 20 | args[1]->dev_minor, 21 | args[1]->dev_statname] = count(); 22 | } 23 | 24 | scsi-transport-dispatch 25 | /arg0 != 0 && HASDI(arg0)/ { 26 | @scsi[DEVINFO(arg0)->dev_major, 27 | DEVINFO(arg0)->dev_minor, 28 | DEVINFO(arg0)->dev_statname] = count(); 29 | } 30 | 31 | tick-1s { 32 | printf("%9s %16s %8s %8s SCSI OP/s\n", "DEV_T", "NAME", "VFS OP/s", "BDEV OP/s"); 33 | printa("%3d,%-5d %16s %8@u %@8u %@u\n", @vfs, @bio, @scsi); 34 | 35 | trunc(@vfs); trunc(@bio); trunc(@scsi); 36 | } 37 | -------------------------------------------------------------------------------- /scripts/dtrace/stat.d: -------------------------------------------------------------------------------- 1 | struct stat_info { 2 | long long st_size; 3 | }; 4 | 5 | translator struct stat_info < uintptr_t s > { 6 | st_size = * ((long long*) copyin(s + offsetof(struct stat64_32, st_size), 7 | sizeof (long long))); 8 | }; 9 | 10 | syscall::fstatat64:entry 11 | { 12 | self->filename = copyinstr(arg1); 13 | self->statptr = arg2; 14 | } 15 | 16 | syscall::fstatat64:return 17 | { 18 | printf("STAT %s size: %d\n", self->filename, 19 | xlate < struct stat_info* > (self->statptr)->st_size); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /scripts/dtrace/topphp.d: -------------------------------------------------------------------------------- 1 | ap*:::process-request-entry { 2 | self->uri = copyinstr(arg1); 3 | } 4 | 5 | php*:::function-entry { 6 | self->starttime = timestamp; 7 | } 8 | 9 | php*:::function-return 10 | / self->starttime != 0 / { 11 | this->func = strjoin(copyinstr(arg3), 12 | strjoin(copyinstr(arg4), copyinstr(arg0))); 13 | @[this->func, self->uri] = avg(timestamp - self->starttime); 14 | } 15 | 16 | END { 17 | printa(@); 18 | } -------------------------------------------------------------------------------- /scripts/dtrace/web.d: -------------------------------------------------------------------------------- 1 | #pragma D option strsize=2048 2 | #pragma D option bufsize=128M 3 | #pragma D option switchrate=20hz 4 | 5 | ap*:::internal-redirect { 6 | printf("[httpd] redirect\t'%s' -> '%s'\n", copyinstr(arg0), copyinstr(arg1)); 7 | } 8 | 9 | ap*:::read-request-entry { 10 | printf("[httpd] read-request\n"); 11 | } 12 | 13 | ap*:::read-request-success { 14 | this->servername = (arg3) ? copyinstr(arg3) : "???"; 15 | 16 | printf("[httpd] read-request\t%s %s %s [status: %d]\n", 17 | copyinstr(arg1), this->servername, copyinstr(arg2), arg4); 18 | } 19 | 20 | ap*:::process-request-entry { 21 | printf("[httpd] process-request\t'%s'\n", copyinstr(arg1)); 22 | } 23 | 24 | ap*:::process-request-return { 25 | printf("[httpd] process-request\t'%s' access-status: %d\n", 26 | copyinstr(arg1), arg2); 27 | } 28 | 29 | php*:::request-startup, 30 | php*:::request-shutdown { 31 | printf("[ PHP ] %s\t%s '%s' file: %s \n", probename, 32 | copyinstr(arg2), copyinstr(arg1), copyinstr(arg0)); 33 | } 34 | 35 | php*:::function-entry, 36 | php*:::function-return { 37 | printf("[ PHP ] %s\t%s%s%s file: %s:%d \n", probename, 38 | copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), 39 | basename(copyinstr(arg1)), arg2); 40 | } 41 | 42 | mysql*:::query-parse-start { 43 | self->parsequery = copyinstr(arg0, 1024); 44 | } 45 | 46 | mysql*:::query-parse-done { 47 | printf("[MySQL] query-parse\t'%s' status: %d\n", self->parsequery, arg0); 48 | } 49 | 50 | mysql*:::query-exec-start { 51 | self->execquery = copyinstr(arg0, 1024); 52 | } 53 | 54 | mysql*:::query-exec-done { 55 | printf("[MySQL] query-exec\t'%s' status: %d\n", self->execquery, arg0); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /scripts/dtrace/wstat.d: -------------------------------------------------------------------------------- 1 | #pragma D option aggsortkey 2 | #pragma D option aggsortkeypos=0 3 | 4 | syscall::write:entry 5 | { 6 | @wbytes[pid, execname, arg0] = sum(arg2); 7 | @wops[pid, execname, arg0] = count(); 8 | } 9 | 10 | tick-1s 11 | { 12 | normalize(@wbytes, 1024); 13 | 14 | printf("%5s %12s %3s %7s %7s\n", 15 | "PID", "EXECNAME", "FD", "OPS", "KBYTES"); 16 | printa("%5u %12s %3u %7@d %7@dK\n", @wops, @wbytes); 17 | clear(@wbytes); 18 | } -------------------------------------------------------------------------------- /scripts/src/hellouser.py: -------------------------------------------------------------------------------- 1 | import os 2 | print "Hello, " + os.getenv('LOGNAME', 'anonymous') + '!' 3 | 4 | -------------------------------------------------------------------------------- /scripts/src/java/Greeter.java: -------------------------------------------------------------------------------- 1 | public class Greeter { 2 | public static void main(String[] args) { 3 | Greeting greeting = new Greeting(); 4 | GreetingThread threads[] = new GreetingThread[4]; 5 | 6 | for(int i = 0; i < 4; ++i) { 7 | threads[i] = new GreetingThread(greeting); 8 | threads[i].start(); 9 | } 10 | 11 | for(int i = 0; i < 4; ++i) { 12 | try { 13 | threads[i].join(); 14 | } 15 | catch(InterruptedException ie) { 16 | } 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /scripts/src/java/Greeting.java: -------------------------------------------------------------------------------- 1 | public class Greeting { 2 | public synchronized void greet() { 3 | System.out.println("Hello, DTrace!"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /scripts/src/java/GreetingThread.java: -------------------------------------------------------------------------------- 1 | class GreetingThread extends Thread { 2 | Greeting greeting; 3 | 4 | GreetingThread(Greeting greeting) { 5 | this.greeting = greeting; 6 | super.setDaemon(true); 7 | } 8 | 9 | public void run() { 10 | while(true) { 11 | greeting.greet(); 12 | try { 13 | Thread.sleep(1000); 14 | } catch (InterruptedException e) { 15 | } 16 | } 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /scripts/src/jsdt/Greeting.java: -------------------------------------------------------------------------------- 1 | public class Greeting { 2 | GreetingProvider provider; 3 | 4 | public Greeting(GreetingProvider provider) { 5 | this.provider = provider; 6 | } 7 | 8 | public void greet(int greetingId) { 9 | provider.greetingStart(greetingId); 10 | System.out.println("Hello DTrace!"); 11 | provider.greetingEnd(greetingId); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /scripts/src/jsdt/GreetingProvider.java: -------------------------------------------------------------------------------- 1 | import com.sun.tracing.Provider; 2 | 3 | public interface GreetingProvider extends Provider { 4 | public void greetingStart(int greetingId); 5 | public void greetingEnd(int greetingId); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /scripts/src/jsdt/JSDT.java: -------------------------------------------------------------------------------- 1 | import com.sun.tracing.*; 2 | 3 | public class JSDT { 4 | static public void main(String[] args) { 5 | ProviderFactory providerFactory = 6 | new sun.tracing.dtrace.DTraceProviderFactory(); 7 | GreetingProvider greetingProvider = (GreetingProvider) 8 | providerFactory.createProvider(GreetingProvider.class); 9 | 10 | Greeting greeter = new Greeting(greetingProvider); 11 | 12 | for(int id = 0; id < 100; ++id) { 13 | greeter.greet(id); 14 | 15 | try { Thread.sleep(500); } 16 | catch(InterruptedException ie) {} 17 | } 18 | 19 | greetingProvider.dispose(); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /scripts/src/lab3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | while(--argc > 0) { 7 | memset(argv[argc], 'X', strlen(argv[argc])); 8 | } 9 | 10 | open("/etc/passwd", O_RDONLY); 11 | return 0; 12 | } -------------------------------------------------------------------------------- /scripts/src/openproc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import re 4 | import sys 5 | 6 | # ---------------------------- 7 | # openproc.py - Collect data from opentrace.py and merge :entry and :return probes 8 | 9 | # Open trace file or use stdin 10 | try: 11 | inf = file(sys.argv[1], 'r') 12 | except OSError as ose: 13 | print ose 14 | print '''openproc.py [filename]''' 15 | sys.exit(1) 16 | except IndexError: 17 | inf = sys.stdin 18 | 19 | # Convert time to human time 20 | def human_time(ns): 21 | ns = float(ns) 22 | for unit in ['ns', 'us', 'ms']: 23 | if abs(ns) < 1000.0: 24 | break 25 | ns /= 1000.0 26 | else: 27 | unit = 's' 28 | return "%.2f %s" % (ns, unit) 29 | 30 | # Parse /etc/passwd and create UID-to-USERNAME map 31 | uid_name = lambda user: (int(user[2]), user[0]) 32 | users = dict([uid_name(user.split(':')) 33 | for user in file('/etc/passwd')]) 34 | 35 | # Per-PID state - tuples (start time, file name) 36 | state = {} 37 | 38 | # Regular expressions for parsing tracer output 39 | re_entry = re.compile("=> uid: (\d+) pid: (\d+) open: (.*?) (\d+)") 40 | re_return = re.compile("<= uid: (\d+) pid: (\d+) ret: (-?\d+) (\d+)") 41 | 42 | for line in inf: 43 | if line.startswith('=>'): 44 | # :entry probe, extract start time and filename 45 | m = re_entry.match(line) 46 | _, pid, fname, tm = m.groups() 47 | 48 | state[int(pid)] = (int(tm), fname) 49 | elif line.startswith('<='): 50 | # :return probe, get return value, timestamp and print information 51 | m = re_return.match(line) 52 | uid, pid, ret, tm = map(int, m.groups()) 53 | 54 | if pid not in state: 55 | continue 56 | 57 | status = 'FD %d' % ret if ret >= 0 else 'ERROR %d' % ret 58 | 59 | print 'OPEN %s %d %s => %s [%s]' % (users.get(uid, str(uid)), 60 | pid, state[pid][1], status, 61 | human_time(tm - state[pid][0])) 62 | del state[pid] 63 | -------------------------------------------------------------------------------- /scripts/stap/callgraph.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/stap 2 | 3 | global traceme; 4 | 5 | probe syscall.open { 6 | if(pid() != target() || filename != "not_exists") 7 | next; 8 | 9 | traceme = target(); 10 | 11 | printf("=> syscall.open [%s]\n", execname()); 12 | } 13 | 14 | probe syscall.open.return { 15 | if(pid() == target()) { 16 | traceme = 0; 17 | } 18 | } 19 | 20 | probe kernel.function("*@fs/*").call ?, 21 | kernel.function("*@fs/*").return ? { 22 | if(!traceme || traceme != pid()) 23 | next; 24 | 25 | if(!is_return()) { 26 | printf("%s -> %s\n", indent( 1), probefunc()); 27 | } 28 | else { 29 | ret = 0; 30 | if(@defined($return)) 31 | ret = $return; 32 | 33 | printf("%s <- %s [%d]\n", indent(-1), probefunc(), ret); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /scripts/stap/deblock.stp: -------------------------------------------------------------------------------- 1 | global vfstp, biotp; 2 | 3 | probe kernel.function("vfs_write") { 4 | file = $file; 5 | if(!file) next; 6 | 7 | sb = @cast(file, "file")->f_path->mnt->mnt_sb; 8 | if(!sb) next; 9 | 10 | bdev = @cast(sb, "super_block")->s_bdev; 11 | if(bdev) 12 | vfstp[bdev] <<< $count; 13 | } 14 | 15 | probe ioblock.request { 16 | if(bio_rw_num(rw) != BIO_WRITE) 17 | next; 18 | 19 | biotp[bdev] <<< size; 20 | } 21 | 22 | probe timer.s(1) { 23 | printf("%12s %8s BDEV KB/s\n", "BDEV", "VFS KB/s"); 24 | foreach([bdev] in vfstp) { 25 | printf("%12s %8d %d\n", bdevname(bdev), 26 | @sum(vfstp[bdev]) / 1024, 27 | @sum(biotp[bdev]) / 1024); 28 | } 29 | delete vfstp; delete biotp; 30 | } -------------------------------------------------------------------------------- /scripts/stap/forktime.stp: -------------------------------------------------------------------------------- 1 | global tm_fork_start_par[128], tm_fork_start[128], tm_fork_end[128], 2 | tm_exec_start[128], tm_exec_end[128], p_argstr[128]; 3 | global fork[128], postfork[128], exec[128], proc[128]; 4 | 5 | probe syscall.fork { 6 | tm_fork_start_par[tid()] = local_clock_us(); 7 | } 8 | probe syscall.fork.return { 9 | if($return > 1) { 10 | tm_fork_start[$return] = tm_fork_start_par[tid()]; 11 | delete tm_fork_start_par[tid()]; 12 | } 13 | } 14 | probe kprocess.start { 15 | tm_fork_end[pid()] = local_clock_us(); 16 | } 17 | probe kprocess.exec { 18 | p_argstr[pid()] = argstr; 19 | tm_exec_start[pid()] = local_clock_us(); 20 | } 21 | probe kprocess.exec_complete { 22 | tm_exec_end[pid()] = local_clock_us(); 23 | } 24 | probe kprocess.exit { 25 | argstr = p_argstr[pid()]; 26 | 27 | fork[execname(), argstr] <<< tm_fork_end[pid()] - tm_fork_start[pid()]; 28 | postfork[execname(), argstr] <<< tm_exec_start[pid()] - tm_fork_end[pid()]; 29 | exec[execname(), argstr] <<< tm_exec_end[pid()] - tm_exec_start[pid()]; 30 | proc[execname(), argstr] <<< local_clock_us() - tm_exec_end[pid()]; 31 | 32 | delete tm_fork_start[pid()]; delete tm_fork_end[pid()]; 33 | delete tm_exec_start[pid()]; delete tm_exec_end[pid()]; 34 | delete p_argstr[pid()]; 35 | } 36 | 37 | probe timer.s(1) { 38 | printf("%48s %8s %8s %8s %8s\n", 39 | "COMMAND", "FORK", "POSTFORK", "EXEC", "PROC"); 40 | foreach([execname, args] in proc) { 41 | printf("%10s %36s %6dus %6dus %6dus %6dus\n", execname, args, 42 | @avg(fork[execname, args]), 43 | @avg(postfork[execname, args]), 44 | @avg(exec[execname, args]), 45 | @avg(proc[execname, args])); 46 | } 47 | delete fork; delete postfork; delete exec; delete proc; 48 | } -------------------------------------------------------------------------------- /scripts/stap/hotspot.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/stap 2 | 3 | probe hotspot.class_loaded 4 | { 5 | printf("%12s [???] %s\n", name, class); 6 | } 7 | 8 | probe hotspot.method_entry, hotspot.method_return 9 | { 10 | printf("%12s [%3d] %s.%s\n", name, thread_id, class, method); 11 | } 12 | 13 | probe hotspot.thread_start, hotspot.thread_stop 14 | { 15 | printf("%12s [%3d] %s\n", name, id, thread_name); 16 | } 17 | 18 | probe hotspot.monitor_contended_enter, hotspot.monitor_contended_exit 19 | { 20 | printf("%12s [%3d] %s\n", name, thread_id, class); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /scripts/stap/kmemstat.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/stap 2 | 3 | global allocs, frees; 4 | 5 | probe kernel.function("kmem_cache_alloc"), 6 | kernel.function("kmem_cache_alloc_node") { 7 | cache = @choose_defined($s, $cachep); 8 | name = kernel_string(@cast(cache, "struct kmem_cache")->name); 9 | 10 | allocs[name] <<< 1; 11 | } 12 | 13 | probe kernel.function("kmem_cache_free") { 14 | cache = @choose_defined($s, $cachep); 15 | name = kernel_string(@cast(cache, "struct kmem_cache")->name); 16 | 17 | frees[name] <<< 1; 18 | } 19 | 20 | probe timer.s(1) { 21 | printf("%8s %8s %s\n", "ALLOCS", "FREES", "SLAB"); 22 | foreach([cache] in allocs) { 23 | printf("%8u %8u %s\n", @count(allocs[cache]), 24 | @count(frees[cache]), cache); 25 | } 26 | 27 | delete allocs; 28 | delete frees; 29 | } 30 | -------------------------------------------------------------------------------- /scripts/stap/lstat.stp: -------------------------------------------------------------------------------- 1 | probe lstat { 2 | printf("%s %d\n", filename, size); 3 | } 4 | 5 | -------------------------------------------------------------------------------- /scripts/stap/mtxtime.stp: -------------------------------------------------------------------------------- 1 | global mtxtime[128], mtxlockt; 2 | 3 | @define libpthread %( "/lib64/libpthread.so.0" %) 4 | @define tsexperiment %( "/opt/tsload/bin/tsexperiment" %) 5 | 6 | probe process(@libpthread).mark("mutex_entry") { 7 | if(pid() != target()) next; 8 | 9 | mtxtime[tid()] = local_clock_ns(); 10 | } 11 | 12 | probe process(@libpthread).mark("mutex_acquired") { 13 | if(pid() != target()) next; 14 | 15 | tm = mtxtime[tid()]; 16 | if(tm == 0) next; 17 | 18 | mtxlockt[ucallers(6)] <<< local_clock_ns() - tm; 19 | delete mtxtime[tid()]; 20 | } 21 | 22 | probe process(@tsexperiment).function("experiment_unconfigure") { 23 | foreach([ub] in mtxlockt) { 24 | if(@count(mtxlockt[ub]) < 100) 25 | continue; 26 | 27 | println("-=-=-=-=-=-=-=-=-=-=-=-=-"); 28 | print_usyms(ub); 29 | print(@hist_log(mtxlockt[ub])); 30 | } 31 | } -------------------------------------------------------------------------------- /scripts/stap/openaggr.stp: -------------------------------------------------------------------------------- 1 | global open, creat, success 2 | global O_CREAT = 64; 3 | 4 | probe syscall.open { 5 | if(flags & O_CREAT) 6 | creat[execname(), pid()] <<< 1; 7 | else 8 | open[execname(), pid()] <<< 1; 9 | } 10 | 11 | probe syscall.open.return { 12 | if($return >= 0) 13 | success[execname(), pid()] <<< 1; 14 | } 15 | 16 | probe timer.s($1) { 17 | println(ctime(gettimeofday_s())); 18 | 19 | printf("%12s %6s %6s %6s %s\n", 20 | "EXECNAME", "PID", "CREATE", "OPEN", "SUCCESS"); 21 | foreach([en, pid+] in open) { 22 | printf("%12s %6d %6d %6d %d\n", 23 | en, pid, @count(creat[en, pid]), @count(open[en, pid]), 24 | @count(success[en, pid])); 25 | } 26 | 27 | delete open; delete creat; delete success; 28 | } -------------------------------------------------------------------------------- /scripts/stap/opentrace.stp: -------------------------------------------------------------------------------- 1 | global O_WRONLY = 1; 2 | global O_RDWR = 2; 3 | global O_APPEND = 1024; 4 | global O_CREAT = 64; 5 | 6 | global t_path, t_flags; 7 | 8 | probe syscall.open { 9 | t_path[tid()] = filename; 10 | t_flags[tid()] = flags; 11 | } 12 | 13 | probe syscall.open.return { 14 | flags = t_flags[tid()]; 15 | 16 | if(flags & O_RDWR) { 17 | flags_str = "O_RDWR"; 18 | } 19 | else if(flags & O_WRONLY) { 20 | flags_str = "O_WRONLY"; 21 | } 22 | else { 23 | flags_str = "O_RDONLY"; 24 | } 25 | if(flags & O_APPEND) { 26 | flags_str = sprintf("%s|%s", flags_str, "O_APPEND"); 27 | } 28 | if(flags & O_CREAT) { 29 | flags_str = sprintf("%s|%s", flags_str, "O_CREAT"); 30 | } 31 | 32 | printf("%s[%d(%d:%d)] open(%s, %s) = %d\n", 33 | execname(), pid(), uid(), gid(), 34 | t_path[tid()], flags_str, $return); 35 | } -------------------------------------------------------------------------------- /scripts/stap/pagefault.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/stap 2 | 3 | /** 4 | * pagefault.stp 5 | * 6 | * Traces page faults handled by handle_mm_fault() 7 | * 8 | * Tested on Linux 3.10 (CentOS 7) 9 | */ 10 | 11 | global fault_flags; 12 | global vma_flags; 13 | 14 | probe begin { 15 | /* See include/linux/mm.h */ 16 | fault_flags[0] = "WRITE"; fault_flags[1] = "NONLINEAR"; 17 | fault_flags[2] = "MKWRITE"; fault_flags[3] = "ALLOW_RETRY"; 18 | fault_flags[4] = "RETRY_NOWAIT"; fault_flags[5] = "KILLABLE"; 19 | 20 | vma_flags[0] = "VM_GROWSDOWN"; vma_flags[2] = "VM_PFNMAP"; 21 | vma_flags[3] = "VM_DENYWRITE"; vma_flags[5] = "VM_LOCKED"; 22 | vma_flags[6] = "VM_IO"; vma_flags[7] = "VM_SEQ_READ"; 23 | vma_flags[8] = "VM_RAND_READ"; vma_flags[9] = "VM_DONTCOPY"; 24 | vma_flags[10] = "VM_DONTEXPAND"; vma_flags[12] = "VM_ACCOUNT"; 25 | vma_flags[13] = "VM_NORESERVE"; vma_flags[14] = "VM_HUGETLB"; 26 | vma_flags[15] = "VM_NONLINEAR"; vma_flags[16] = "VM_ARCH_1"; 27 | vma_flags[18] = "VM_DONTDUMP"; vma_flags[20] = "VM_MIXEDMAP"; 28 | vma_flags[21] = "VM_HUGEPAGE"; vma_flags[22] = "VM_NOHUGEPAGE"; 29 | } 30 | 31 | function prot_str:string(prot: long) { 32 | return sprintf("%s%s%s%s", 33 | (prot & 0x1) ? "r" : "-", 34 | (prot & 0x2) ? "w" : "-", 35 | (prot & 0x4) ? "x" : "-", 36 | (prot & 0x8) ? "s" : "-"); 37 | } 38 | 39 | function vma_flags_str:string(flags: long) { 40 | prot = flags & 0xf; 41 | mprot = (flags >> 4) & 0xf; 42 | flags = flags >> 8; 43 | 44 | for(i = 0; i < 23; ++i) { 45 | if(flags & 1) { 46 | str = sprintf("%s|%s", str, vma_flags[i]); 47 | } 48 | 49 | flags >>= 1; 50 | } 51 | 52 | return sprintf("prot: %s may: %s flags: %s", 53 | prot_str(prot), prot_str(mprot), 54 | substr(str, 1, strlen(str) - 1)); 55 | } 56 | 57 | function fault_flags_str:string(flags: long) { 58 | for(i = 0; i < 6; ++i) { 59 | if(flags & 1) { 60 | str = sprintf("%s|%s", str, fault_flags[i]); 61 | } 62 | 63 | flags >>= 1; 64 | } 65 | 66 | /* Cut first pipe sign ('|') */ 67 | return substr(str, 1, strlen(str) - 1); 68 | } 69 | 70 | function vm_fault_str(fault_type: long) { 71 | if(vm_fault_contains(fault_type, VM_FAULT_OOM)) 72 | return "OOM"; 73 | else if(vm_fault_contains(fault_type, VM_FAULT_SIGBUS)) 74 | return "SIGBUS"; 75 | else if(vm_fault_contains(fault_type, VM_FAULT_MINOR)) 76 | return "MINOR"; 77 | else if(vm_fault_contains(fault_type, VM_FAULT_MAJOR)) 78 | return "MAJOR"; 79 | else if(vm_fault_contains(fault_type, VM_FAULT_NOPAGE)) 80 | return "NOPAGE"; 81 | else if(vm_fault_contains(fault_type, VM_FAULT_LOCKED)) 82 | return "LOCKED"; 83 | else if(vm_fault_contains(fault_type, VM_FAULT_ERROR)) 84 | return "ERROR"; 85 | 86 | return "???"; 87 | } 88 | 89 | probe vm.pagefault { 90 | printf("vm.pagefault pid: %d mm: %p\n", pid(), $mm); 91 | printf("\taddr: %p flags: %s\n", $address, fault_flags_str($flags)); 92 | printf("\tVMA [%p:%p]\n", $vma->vm_start, $vma->vm_end); 93 | printf("\t%s\n", vma_flags_str($vma->vm_flags)); 94 | printf("\tamp: %p\n", $vma->anon_vma) 95 | 96 | if($vma->vm_file != 0) 97 | printf("\tfile: %s\n", d_name($vma->vm_file->f_path->dentry)) 98 | } 99 | 100 | probe vm.pagefault.return { 101 | printf("\t => pid: %d pf: %s\n", pid(), vm_fault_str(fault_type)); 102 | } 103 | -------------------------------------------------------------------------------- /scripts/stap/pfstat.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/stap 2 | 3 | global pfs; 4 | 5 | probe vm.pagefault { 6 | vm_file = "???"; 7 | if($vma->vm_file != 0) 8 | vm_file = d_name($vma->vm_file->f_path->dentry); 9 | 10 | pfs[vm_file] <<< 1; 11 | } 12 | 13 | probe timer.s(1) { 14 | printf("%8s %s\n", "FAULTS", "VMFILE"); 15 | foreach([vm_file] in pfs) { 16 | printf("%8u %s\n", @count(pfs[vm_file]), vm_file); 17 | } 18 | 19 | delete pfs; 20 | } 21 | -------------------------------------------------------------------------------- /scripts/stap/proc.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/stap 2 | 3 | probe scheduler.process*, scheduler.wakeup_new, syscall.fork, 4 | syscall.exec*, syscall.exit, syscall.wait*, kprocess.* { 5 | printf("%6d[%8s]/%6d[%8s] %s\n", 6 | pid(), execname(), ppid(), pid2execname(ppid()), pn()); 7 | } 8 | 9 | probe scheduler.process_fork { 10 | printf("\tPID: %d -> %d\n", parent_pid, child_pid); 11 | } 12 | 13 | probe kprocess.exec { 14 | printf("\tfilename: %s\n", filename); 15 | } 16 | 17 | probe kprocess.exit { 18 | printf("\treturn code: %d\n", code); 19 | } 20 | -------------------------------------------------------------------------------- /scripts/stap/pthread.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap 2 | 3 | @define libpthread %( "/lib64/libpthread.so.0" %) 4 | @define libc %( "/lib64/libc.so.6" %) 5 | 6 | probe process(@libpthread).mark("pthread_create") { 7 | if(pid() != target()) next; 8 | 9 | thread_id = user_long($arg1); 10 | thread_caller = usymname($arg3); 11 | thread_arg = $arg4; 12 | 13 | printf("[%d] pthread_create %x %s(%x)\n", tid(), 14 | thread_id, thread_caller, thread_arg); 15 | } 16 | 17 | probe process(@libpthread).mark("pthread_start") { 18 | if(pid() != target()) next; 19 | 20 | thread_id = $arg1; 21 | printf("[%d] pthread_start %x\n", tid(), thread_id); 22 | } 23 | 24 | probe process(@libpthread).mark("pthread_join") { 25 | if(pid() != target()) next; 26 | 27 | thread_id = $arg1; 28 | printf("[%d] pthread_join %x\n", tid(), thread_id); 29 | } 30 | 31 | probe process(@libpthread).mark("pthread_join_ret") { 32 | if(pid() != target()) next; 33 | 34 | thread_id = $arg1; 35 | printf("[%d] pthread_join %x return -> %d/%d \n", tid(), 36 | thread_id, $arg2, $arg3); 37 | } 38 | 39 | probe process(@libpthread).mark("mutex_*"), 40 | process(@libpthread).mark("cond_*"), 41 | process(@libpthread).mark("rdlock_*"), 42 | process(@libpthread).mark("wrlock_*"), 43 | process(@libpthread).mark("rwlock_*") { 44 | if(pid() != target()) next; 45 | 46 | printf("[%d] %s %p\n", tid(), pn(), $arg1); 47 | print_ustack(ucallers(5)); 48 | } 49 | 50 | probe process(@libpthread).mark("lll_*"), 51 | process(@libc).mark("lll_*") { 52 | if(pid() != target()) next; 53 | 54 | printf("[%d] %s\n", tid(), pn()); 55 | print_ustack(ucallers(5)); 56 | } 57 | -------------------------------------------------------------------------------- /scripts/stap/pycode.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap 2 | 3 | @define PYTHON3_LIBRARY %( "/usr/lib64/libpython3.4m.so.1.0" %) 4 | 5 | function get_py3_string:string(uo: long) { 6 | flags = user_uint32(&@cast(uo, "PyASCIIObject", @PYTHON3_LIBRARY)->state); 7 | if(flags & 0xE0) { 8 | size = &@cast(0, "PyASCIIObject", @PYTHON3_LIBRARY)[1] 9 | return user_string(uo + size); 10 | } 11 | 12 | return "???"; 13 | } 14 | 15 | probe process(@PYTHON3_LIBRARY).function("PyEval_EvalCodeEx") { 16 | code = $_co; 17 | if(code) { 18 | printf("%s %s:%d\n", 19 | get_py3_string(@cast(code, "PyCodeObject", @PYTHON3_LIBRARY)->co_name), 20 | get_py3_string(@cast(code, "PyCodeObject", @PYTHON3_LIBRARY)->co_filename), 21 | @cast(code, "PyCodeObject", @PYTHON3_LIBRARY)->co_firstlineno); 22 | } 23 | } -------------------------------------------------------------------------------- /scripts/stap/pymalloc.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap 2 | 3 | @define libc %( "/lib64/libc.so.6" %) 4 | 5 | global file_stack, func_stack, mallocs, thread_depth 6 | 7 | probe python.function.entry { 8 | thread_depth[tid()]++; 9 | 10 | depth = thread_depth[tid()]; 11 | file_stack[tid(), depth] = filename; 12 | func_stack[tid(), depth] = funcname; 13 | } 14 | 15 | probe python.function.return { 16 | thread_depth[tid()]--; 17 | } 18 | 19 | probe process(@libc).function("_int_malloc") { 20 | depth = thread_depth[tid()]; 21 | mallocs[file_stack[tid(), depth], 22 | func_stack[tid(), depth]] <<< $bytes; 23 | } -------------------------------------------------------------------------------- /scripts/stap/readahead.stp: -------------------------------------------------------------------------------- 1 | global vfsops, bioops, scsiops; 2 | 3 | probe kernel.function("vfs_read") { 4 | file = $file; 5 | if(!file) next; 6 | 7 | sb = @cast(file, "file")->f_path->mnt->mnt_sb; 8 | if(!sb) next; 9 | 10 | bdev = @cast(sb, "super_block")->s_bdev; 11 | if(bdev) 12 | vfsops[bdev] <<< 1; 13 | } 14 | 15 | probe ioblock.request { 16 | if(bio_rw_num(rw) != BIO_READ) 17 | next; 18 | 19 | bioops[bdev] <<< 1; 20 | } 21 | 22 | probe scsi.ioentry { 23 | bio = @cast(req_addr, "struct request")->bio; 24 | if(!bio) next; 25 | 26 | bdev = @cast(bio, "bio")->bi_bdev; 27 | if(bdev) 28 | scsiops[bdev] <<< 1; 29 | } 30 | 31 | probe timer.s(1) { 32 | printf("%12s %8s %8s SCSI OP/s\n", "BDEV", "VFS OP/s", "BDEV OP/s"); 33 | foreach([bdev] in vfsops) { 34 | printf("%12s %8d %8d %d\n", bdevname(bdev), @count(vfsops[bdev]), 35 | @count(bioops[bdev]), @count(scsiops[bdev])); 36 | } 37 | delete vfsops; delete bioops; delete scsiops; 38 | } -------------------------------------------------------------------------------- /scripts/stap/scsitrace.stp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/stap 2 | 3 | global rqs, bio2rq, specs, times; 4 | 5 | function probe_print:string(bio:long) { 6 | return sprintf("%-24s %p cpu%d %u\n", pn(), bio, cpu(), 7 | gettimeofday_ns() - times[bio2rq[bio]]); 8 | } 9 | 10 | function rq_probe_print(rq:long, bio:long) { 11 | if(bio == 0) 12 | bio = @cast(rq, "struct request")->bio; 13 | return sprintf("%-24s %p %p cpu%d %u\n", pn(), bio, rq, cpu(), 14 | gettimeofday_ns() - times[bio]); 15 | } 16 | 17 | function proc_print:string() { 18 | return sprintf("\tPROC: %d/%d %s\n", pid(), tid(), execname()); 19 | } 20 | 21 | function handle_bio2rq(bio:long, rq:long) { 22 | if(specs[rq] == 0) { 23 | specs[rq] = speculation(); 24 | } 25 | 26 | rqs[rq] += 1; 27 | bio2rq[bio] = rq; 28 | 29 | speculate(specs[rq], 30 | rq_probe_print(rq, bio) 31 | .proc_print() 32 | .sprintf("\tBUF flags: %s %x count: %d blkno: %d comp: %s\n", 33 | bio_rw_str(@cast(bio, "bio")->bi_rw), @cast(bio, "bio")->bi_flags, 34 | @cast(bio, "bio")->bi_size, @cast(bio, "bio")->bi_sector, 35 | symname(@cast(bio, "bio")->bi_end_io)) 36 | .sprintf("\tDEV %d,%d\tINO %d\n", MAJOR(@cast(bio, "bio")->bi_bdev->bd_dev), 37 | MINOR(@cast(bio, "bio")->bi_bdev->bd_dev), __bio_ino(bio))); 38 | } 39 | 40 | probe ioblock.request { 41 | times[$bio] = gettimeofday_ns(); 42 | } 43 | 44 | probe kernel.function("bio_attempt_front_merge").return, 45 | kernel.function("bio_attempt_back_merge").return { 46 | if($return) { 47 | /* BIO was merged with request */ 48 | rq = $req; 49 | bio = $bio; 50 | 51 | if(bio == 0) next; 52 | 53 | handle_bio2rq(bio, rq); 54 | } 55 | } 56 | 57 | probe kernel.function("get_request").return { 58 | rq = $return; 59 | bio = $bio; 60 | 61 | if(bio == 0) next; 62 | 63 | /* BIO were created a new request */ 64 | handle_bio2rq(bio, rq); 65 | } 66 | 67 | probe ioscheduler.elv_add_request, ioscheduler.elv_completed_request { 68 | if(rq == 0 || specs[rq] == 0) next; 69 | speculate(specs[rq], 70 | rq_probe_print(rq, 0) 71 | .sprintf("\tDEV %d,%d\n", disk_major, disk_minor)); 72 | } 73 | 74 | probe scsi.ioentry, scsi.iodone, scsi.iocompleted, scsi.iodispatching { 75 | if(req_addr == 0 || specs[req_addr] == 0) next; 76 | speculate(specs[req_addr], 77 | rq_probe_print(req_addr, 0)); 78 | } 79 | 80 | probe scsi.iodispatching { 81 | if(req_addr == 0 || specs[req_addr] == 0) next; 82 | speculate(specs[req_addr], 83 | rq_probe_print(req_addr, 0) 84 | .sprintf("\tSCSI DEV %d:%d:%d:%d %s\n", 85 | host_no, channel, lun, dev_id, device_state_str) 86 | .sprintf("\tSCSI PKT flags: %x comp: %s\n", 87 | @cast(req_addr, "struct request")->cmd_flags, 88 | symname($cmd->scsi_done))); 89 | } 90 | 91 | probe ioblock.end { 92 | bio = $bio; 93 | rq = bio2rq[bio]; 94 | 95 | delete bio2rq[bio]; 96 | delete times[bio]; 97 | 98 | rqs[rq] -= 1; 99 | if(rqs[rq] == 0) { 100 | speculate(specs[rq], probe_print(bio)); 101 | speculate(specs[rq], "----------\n"); 102 | commit(specs[rq]); 103 | 104 | delete specs[rq]; 105 | } 106 | } -------------------------------------------------------------------------------- /scripts/stap/tapset/lstat.stp: -------------------------------------------------------------------------------- 1 | probe lstat = kernel.function("sys_lstat64").return ? , 2 | kernel.function("sys32_lstat64").return ? { 3 | filename = user_string($filename); 4 | size = user_uint64(& @cast($statbuf, "struct stat64")->st_size); 5 | } 6 | 7 | probe lstat = kernel.function("sys_newlstat").return ? { 8 | filename = user_string($filename); 9 | %( arch == "x86_64" 10 | %? size = user_uint64(& @cast($statbuf, "struct stat")->st_size); 11 | %: size = user_uint32(& @cast($statbuf, "struct stat")->st_size); 12 | %) 13 | } -------------------------------------------------------------------------------- /scripts/stap/topphp.stp: -------------------------------------------------------------------------------- 1 | @define httpd %( "/usr/local/apache2/bin/httpd" %) 2 | @define libphp5 %( "/usr/local/apache2/modules/libphp5.so" %) 3 | 4 | global rquri, starttime, functime; 5 | 6 | probe process(@httpd).mark("process__request__entry") { 7 | rquri[tid()] = user_string($arg2); 8 | } 9 | 10 | probe process(@libphp5).mark("function__entry") { 11 | starttime[tid()] = gettimeofday_ns(); 12 | } 13 | 14 | probe process(@libphp5).mark("function__return") { 15 | if(starttime[tid()] == 0) next; 16 | 17 | func = user_string($arg4) . user_string($arg5) . user_string($arg1); 18 | functime[func, rquri[tid()]] <<< (gettimeofday_ns() - starttime[tid()]); 19 | } 20 | 21 | probe end { 22 | foreach([func, uri] in functime) { 23 | printf("%40s %32s %8d %d\n", func, uri, 24 | @count(functime[func, uri]), @avg(functime[func, uri])); 25 | } 26 | } -------------------------------------------------------------------------------- /scripts/stap/web.stp: -------------------------------------------------------------------------------- 1 | @define httpd %( "/usr/local/apache2/bin/httpd" %) 2 | @define libphp5 %( "/usr/local/apache2/modules/libphp5.so" %) 3 | @define mysqld %( "/usr/local/mysql/bin/mysqld" %) 4 | 5 | global parsequery; 6 | global execquery; 7 | 8 | function basename:string(s:string) { 9 | len = strlen(s) 10 | i = len 11 | 12 | while(i > 0) { 13 | /* 47 is code for '/' */ 14 | if(stringat(s, i - 1) == 47) 15 | return substr(s, i, len - i); 16 | 17 | --i; 18 | } 19 | 20 | return s; 21 | } 22 | 23 | probe process(@httpd).mark("internal__redirect") { 24 | printf("[httpd] redirect\t'%s' -> '%s'\n", 25 | user_string($arg1), user_string($arg2)); 26 | } 27 | 28 | probe process(@httpd).mark("read__request__entry") { 29 | printf("[httpd] read-request\n"); 30 | } 31 | 32 | probe process(@httpd).mark("read__request__success") { 33 | servername = ($arg4) ? user_string($arg4) : "???"; 34 | 35 | printf("[httpd] read-request\t%s %s %s [status: %d]\n", 36 | user_string($arg2), servername, user_string($arg3), $arg5); 37 | } 38 | 39 | probe process(@httpd).mark("process__request__entry") { 40 | printf("[httpd] process-request\t'%s'\n", user_string($arg2)); 41 | } 42 | 43 | probe process(@httpd).mark("process__request__return") { 44 | printf("[httpd] process-request\t'%s' access-status: %d\n", 45 | user_string($arg2), $arg3); 46 | } 47 | 48 | probe process(@libphp5).mark("request__startup"), 49 | process(@libphp5).mark("request__shutdown") { 50 | printf("[ PHP ] %s\n\t%s '%s' file: %s \n", pn(), user_string($arg3), 51 | user_string($arg2), user_string($arg1)); 52 | } 53 | 54 | probe process(@libphp5).mark("function__entry"), 55 | process(@libphp5).mark("function__return") { 56 | printf("[ PHP ] %s\n\t%s%s%s file: %s:%d \n", pn(), 57 | user_string($arg4), user_string($arg5), user_string($arg1), 58 | basename(user_string($arg2)), $arg3); 59 | } 60 | 61 | probe process(@mysqld).mark("query__parse__start") { 62 | parsequery[tid()] = user_string_n($arg1, 1024); 63 | } 64 | 65 | probe process(@mysqld).mark("query__parse__done") { 66 | printf("[MySQL] query-parse\t'%s' status: %d\n", parsequery[tid()], $arg1); 67 | } 68 | 69 | probe process(@mysqld).mark("query__exec__start") { 70 | execquery[tid()] = user_string_n($arg1, 1024); 71 | } 72 | 73 | probe process(@mysqld).mark("query__exec__done") { 74 | printf("[MySQL] query-exec\t'%s' status: %d\n", execquery[tid()], $arg1); 75 | } 76 | 77 | -------------------------------------------------------------------------------- /scripts/stap/wqtrace.stp: -------------------------------------------------------------------------------- 1 | probe kernel.function("prepare_to_wait*"), 2 | kernel.function("add_wait_queue*") { 3 | if(pid() == stp_pid()) next; 4 | 5 | state = -1; 6 | if(@defined($state)) 7 | state = $state; 8 | 9 | printf("[%d]%s %s:%s\n\twq head: %p wq: %p\n", 10 | pid(), execname(), symname(caller_addr()), 11 | probefunc(), $q, $wait); 12 | printf("\ttsk: %p state: %x func: %s\n", 13 | $wait->private, state, symname($wait->func)); 14 | } 15 | 16 | probe kernel.function("wait_for_completion*") { 17 | if(pid() == stp_pid()) next; 18 | 19 | timeout = 0; 20 | if(@defined($timeout)) 21 | timeout = $timeout; 22 | 23 | printf("[%d]%s %s:%s\n\tcompletion: %pwq head: %p timeout: %d\n", 24 | pid(), execname(), symname(caller_addr()), 25 | probefunc(), $x, &$x->wait, timeout); 26 | } 27 | 28 | probe kernel.function("wait_for_completion*").return { 29 | if(pid() == stp_pid()) next; 30 | 31 | printf("[%d]%s %s:%s\n\tcompletion: %p\n", 32 | pid(), execname(), symname(caller_addr()), probefunc(), $x); 33 | } 34 | 35 | probe kernel.function("finish_wait"), 36 | kernel.function("remove_wait_queue") { 37 | if(pid() == stp_pid()) next; 38 | 39 | printf("[%d]%s %s:%s\n\twq head: %p wq: %p\n", 40 | pid(), execname(), symname(caller_addr()), 41 | probefunc(), $q, $wait); 42 | } 43 | 44 | probe kernel.function("complete"), 45 | kernel.function("complete_all") { 46 | if(pid() == stp_pid()) next; 47 | 48 | printf("[%d]%s %s:%s\n\tcompletion: %p wq head: %p\n", 49 | pid(), execname(), symname(caller_addr()), 50 | probefunc(), $x, &$x->wait); 51 | } 52 | 53 | probe kernel.function("__wake_up"), 54 | kernel.function("__wake_up_locked*"), 55 | kernel.function("__wake_up_sync*") { 56 | if(pid() == stp_pid()) next; 57 | 58 | nr = -1 59 | if(@defined($nr_exclusive)) 60 | nr = $nr_exclusive; 61 | if(@defined($nr)) 62 | nr = $nr; 63 | 64 | printf("[%d]%s %s:%s\n\twq head: %p state: %p nr: %d\n", 65 | pid(), execname(), symname(caller_addr()), 66 | probefunc(), $q, $mode, nr); 67 | } 68 | -------------------------------------------------------------------------------- /scripts/stap/wstat.stp: -------------------------------------------------------------------------------- 1 | global wstat; 2 | 3 | probe syscall.write { 4 | wstat[pid(), execname(), fd] <<< count; 5 | } 6 | 7 | probe timer.s(1) { 8 | printf("%5s %12s %3s %7s %7s\n", 9 | "PID", "EXECNAME", "FD", "OPS", "KBYTES"); 10 | 11 | foreach([pid+, execname, fd] in wstat) { 12 | printf("%5d %12s %3d %7u %7u\n", 13 | pid, execname, fd, @count(wstat), 14 | @sum(wstat) / 1024); 15 | } 16 | 17 | delete wstat; 18 | } 19 | -------------------------------------------------------------------------------- /tsdoc/gen-cheatsheet.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from tsdoc.page import MarkdownPage 5 | from tsdoc.blocks.html import HTMLPrinter 6 | from tsdoc.blocks.pdf import PDFPrinter, CheatSheetTemplate 7 | 8 | # Main code 9 | _ = sys.argv.pop(0) 10 | page_path = sys.argv.pop(0) 11 | out_path = sys.argv.pop(0) 12 | 13 | # Destination dir 14 | doc_dir = os.path.dirname(page_path) 15 | 16 | if out_path.endswith('.pdf'): 17 | printer = PDFPrinter(False, CheatSheetTemplate) 18 | elif out_path.endswith('.html'): 19 | printer = HTMLPrinter(os.getenv('TSDOC_HTML_TEMPLATE')) 20 | 21 | page = MarkdownPage(page_path) 22 | page.header = os.getenv('TSDOC_HEADER') 23 | page.docspace = None 24 | 25 | with open(out_path, 'wb') as outf: 26 | if printer.single_doc: 27 | printer.do_print_pages(outf, page.header, [page]) 28 | else: 29 | printer.do_print(outf, page.header, page) 30 | 31 | -------------------------------------------------------------------------------- /tsdoc/gen-doc.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from collections import defaultdict, OrderedDict 5 | 6 | from tsdoc import TSDoc 7 | from tsdoc.page import MarkdownPage, IndexPage 8 | 9 | from tsdoc.blocks import Link 10 | from tsdoc.blocks.markdown import MarkdownPrinter 11 | from tsdoc.blocks.html import HTMLPrinter 12 | from tsdoc.blocks.latex import LatexPrinter 13 | from tsdoc.blocks.pdf import PDFPrinter 14 | from tsdoc.blocks.epub import EpubPrinter 15 | 16 | # Main code 17 | _ = sys.argv.pop(0) 18 | index_path = sys.argv.pop(0) 19 | 20 | # Destination dir 21 | doc_dir = os.path.dirname(index_path) 22 | 23 | # Output format 24 | doc_format = os.getenv('TSDOC_FORMAT', 'html') 25 | doc_header = os.getenv('TSDOC_HEADER', '') 26 | verbose = os.getenv('TSDOC_VERBOSE', None) is not None 27 | 28 | if doc_format == 'html': 29 | doc_suffix = '.html' 30 | printer = HTMLPrinter(os.getenv('TSDOC_HTML_TEMPLATE')) 31 | elif doc_format == 'markdown': 32 | doc_suffix = '.out.md' 33 | printer = MarkdownPrinter() 34 | elif doc_format == 'latex': 35 | doc_suffix = '.tex' 36 | printer = LatexPrinter() 37 | elif doc_format == 'pdf': 38 | doc_suffix = '.pdf' 39 | printer = PDFPrinter() 40 | elif doc_format == 'epub': 41 | doc_suffix = '.epub' 42 | printer = EpubPrinter() 43 | else: 44 | raise ValueError("Invalid documentation format '%s'" % doc_format) 45 | 46 | pages = defaultdict(OrderedDict) 47 | tsdoc_pages = [] 48 | 49 | if verbose: 50 | print 'Parsing ', 51 | 52 | for page_path in sys.argv: 53 | page_name = os.path.basename(page_path) 54 | print page_name, 55 | if page_path.endswith('.md'): 56 | page = MarkdownPage(page_path) 57 | 58 | pages[page.docspace][page.name] = page 59 | 60 | if verbose: 61 | print '\nProcessing', 62 | 63 | for page in tsdoc_pages: 64 | print '%s/%s' % (page.docspace, page.name), 65 | page.process() 66 | 67 | if verbose: 68 | print '\nBuilding index...' 69 | 70 | # Build indexes and Cross-References 71 | index_page = IndexPage(index_path, doc_header, pages) 72 | index_page.generate(printer, doc_dir, doc_suffix) -------------------------------------------------------------------------------- /tsdoc/tsdoc/blocks/markdown.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | from tsdoc.blocks import * 4 | 5 | class MarkdownPrinter(Printer): 6 | single_doc = False 7 | 8 | def __init__(self): 9 | pass 10 | 11 | def do_print(self, stream, header, page): 12 | self.stream = stream 13 | for block in page: 14 | self._print_block(block) 15 | 16 | def _md_filter(self, block, s): 17 | if isinstance(block, Code): 18 | return s 19 | 20 | s = s.replace('_', '\\_') 21 | s = s.replace('*', '\\*') 22 | 23 | return s 24 | 25 | def _last_newline(self, text): 26 | for char in reversed(text): 27 | if char not in string.whitespace: 28 | return False 29 | 30 | if char == '\n': 31 | return True 32 | 33 | return False 34 | 35 | def _print_block(self, block): 36 | if isinstance(block, Paragraph): 37 | self.stream.write('\n') 38 | if isinstance(block, Code): 39 | self.stream.write('\n```\n') 40 | elif isinstance(block, ListEntry): 41 | self.stream.write(' ' * block.level + ' * ') 42 | 43 | text = '' 44 | for part in block: 45 | if isinstance(part, Block): 46 | self._print_block(part) 47 | else: 48 | prefix = '' 49 | suffix = '' 50 | 51 | if isinstance(part, Header): 52 | prefix = '#' * part.size + ' ' 53 | suffix = '\n' 54 | elif isinstance(part, ItalicText): 55 | prefix = suffix = '_' 56 | elif isinstance(part, BoldText): 57 | prefix = suffix = '**' 58 | elif isinstance(part, InlineCode): 59 | prefix = suffix = '`' 60 | elif isinstance(part, Reference): 61 | prefix = '[' 62 | suffix = ']' 63 | elif isinstance(part, Link): 64 | prefix = '[' 65 | 66 | if part.type == Link.INTERNAL: 67 | suffix = '][%s]' % part.where 68 | else: 69 | suffix = '](%s)' % part.where 70 | 71 | text = self._md_filter(block, str(part)) 72 | text = prefix + text + suffix 73 | 74 | self.stream.write(text) 75 | 76 | if isinstance(block, ListEntry): 77 | if not self._last_newline(text): 78 | self.stream.write('\n') 79 | elif isinstance(block, Code): 80 | self.stream.write('\n```\n') 81 | if isinstance(block, Paragraph): 82 | self.stream.write('\n') -------------------------------------------------------------------------------- /tsload/SConstruct: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from SCons.Action import ActionFactory 4 | 5 | PathJoin = os.path.join 6 | PathBaseName = os.path.basename 7 | PathExists = os.path.exists 8 | 9 | from SCons.Errors import StopError 10 | 11 | # Setup path to TSLoad 12 | AddOption('--with-tsload', dest='tsload', action="store", 13 | default=PathJoin('/home/myaut/univer/tsload/agent/build/tsload-0.2.a1-linux2', 'share/tsload/devel'), 14 | metavar='DIR', help='Path to tsload development directory') 15 | 16 | if not GetOption('tsload') or not PathExists(GetOption('tsload')): 17 | raise StopError('Provide path to tsload by specifying --with-tsload option') 18 | 19 | env = DefaultEnvironment(ENV = {'PATH': os.environ['PATH']}) 20 | 21 | env['TSLOAD_DEVEL_PATH'] = GetOption('tsload') 22 | env['TSPROJECT'] = 'file_opener' 23 | env['TSVERSION'] = '0.1' 24 | env['TSNAME'] = env['TSPROJECT'] + '-' + env['TSVERSION'] 25 | 26 | env['TSEXTPATH'] = Dir('#').abspath 27 | 28 | env['VERBOSE_BUILD'] = ['cmdline'] 29 | 30 | SConscript(PathJoin(env['TSLOAD_DEVEL_PATH'], 'SConscript.ext.py'), 'env') 31 | 32 | # ------------ 33 | # MODULES 34 | 35 | modules = ['file_opener', 'proc_starter'] 36 | 37 | for mod in modules: 38 | variant_dir = env.BuildDir(PathJoin('file_opener', mod)) 39 | 40 | SConscript(PathJoin(mod, 'SConscript'), 'env', 41 | variant_dir = variant_dir) -------------------------------------------------------------------------------- /tsload/file_opener/.ctime_cache: -------------------------------------------------------------------------------- 1 | modsrc.c.in 1421059608 2 | modsrc.h.in 1421059608 3 | SConscript.in 1421059608 4 | SConstruct.in 1421059608 5 | -------------------------------------------------------------------------------- /tsload/file_opener/SConscript: -------------------------------------------------------------------------------- 1 | from pathutil import * 2 | 3 | target = 'file_opener' 4 | 5 | Import('env') 6 | 7 | mod = env.Clone() 8 | file_opener = mod.Module('load', target) 9 | 10 | experiment_json = File('experiment.json') 11 | 12 | mod.Depends(file_opener, [experiment_json]) 13 | 14 | install_path = PathJoin(mod['INSTALL_VAR'], target) 15 | mod.InstallTarget(target, install_path, experiment_json) -------------------------------------------------------------------------------- /tsload/file_opener/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file_opener", 3 | "steps": { 4 | "open": { 5 | "num_steps": 20, 6 | "num_requests": 100 7 | } 8 | }, 9 | "threadpools": { 10 | "tp_open" : { 11 | "num_threads": 2, 12 | "quantum": 1000000000, 13 | "disp": { 14 | "type": "round-robin" 15 | } 16 | } 17 | }, 18 | "workloads": { 19 | "open": { 20 | "wltype": "file_opener", 21 | "threadpool": "tp_open", 22 | "rqsched" : { 23 | "type": "iat", 24 | "distribution": "exponential" 25 | }, 26 | "params": { 27 | "root_dir": "/tmp/fopen1", 28 | "created_files": 480, 29 | "max_files": 1024, 30 | "file": { "randgen": { "class": "lcg" } }, 31 | "create": { 32 | "randgen": { "class": "lcg" }, 33 | "pmap" : [ 34 | { "value" : true, "probability" : 0.3 }, 35 | { "value" : false, "probability" : 0.7 } 36 | ] 37 | } 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /tsload/file_opener/include/file_opener.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_OPENER_H_ 2 | #define FILE_OPENER_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | struct file_opener_workload { 9 | char root_dir[1024]; 10 | wlp_integer_t created_files; 11 | wlp_integer_t max_files; 12 | 13 | }; 14 | 15 | struct file_opener_request { 16 | wlp_integer_t file; 17 | wlp_bool_t create; 18 | 19 | }; 20 | #endif /* BUSY_WAIT_H_ */ 21 | 22 | -------------------------------------------------------------------------------- /tsload/file_opener/modinfo.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file_opener", 3 | "wlt_class": [ "fs_op" ], 4 | "has_step": true, 5 | "params": { 6 | "root_dir": { 7 | "_type": "tsload.wlparam.WLParamString", 8 | "description": "Directory where files would be created / deleted", 9 | "len": 1024 10 | }, 11 | "created_files": { 12 | "_type": "tsload.wlparam.WLParamInteger", 13 | "description": "Number of files that would be preliminary created" 14 | }, 15 | "max_files": { 16 | "_type": "tsload.wlparam.WLParamInteger", 17 | "description": "Maximum number of files" 18 | }, 19 | "file": { 20 | "_type": "tsload.wlparam.WLParamInteger", 21 | "request": true, 22 | "description": "ID of file" 23 | }, 24 | "create": { 25 | "_type": "tsload.wlparam.WLParamBoolean", 26 | "request": true, 27 | "description": "Set O_CREAT flag" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tsload/proc_starter/.ctime_cache: -------------------------------------------------------------------------------- 1 | modsrc.c.in 1421244766 2 | modsrc.h.in 1421244766 3 | SConscript.in 1421244766 4 | -------------------------------------------------------------------------------- /tsload/proc_starter/SConscript: -------------------------------------------------------------------------------- 1 | from pathutil import * 2 | import subprocess 3 | 4 | target = 'proc_starter' 5 | 6 | Import('env') 7 | 8 | mod = env.Clone() 9 | 10 | proc_starter = mod.Module('load', target) 11 | 12 | experiment_json = File('experiment.json') 13 | 14 | mod.Depends(proc_starter, [experiment_json]) 15 | 16 | install_path = PathJoin(mod['INSTALL_VAR'], target) 17 | mod.InstallTarget(target, install_path, experiment_json) -------------------------------------------------------------------------------- /tsload/proc_starter/experiment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proc_starter", 3 | "steps": { 4 | "open": { 5 | "num_steps": 10, 6 | "num_requests": 150 7 | } 8 | }, 9 | "threadpools": { 10 | "tp_proc" : { 11 | "num_threads": 12, 12 | "quantum": 1000000000, 13 | "disp": { 14 | "type": "round-robin" 15 | } 16 | } 17 | }, 18 | "workloads": { 19 | "open": { 20 | "wltype": "proc_starter", 21 | "threadpool": "tp_proc", 22 | "rqsched" : { 23 | "type": "iat", 24 | "distribution": "exponential" 25 | }, 26 | "params": { 27 | "num_shells": 12, 28 | "command": { 29 | "randgen": { "class": "lcg" }, 30 | "pmap" : [ 31 | { "probability": 0.4, 32 | "value": "cat /etc/passwd" 33 | }, 34 | { "probability": 0.2, 35 | "value": "ls /var" 36 | }, 37 | { "probability": 0.1, 38 | "value": "ls /usr/lib" 39 | }, 40 | { "probability": 0.2, 41 | "value": "bash -c 'exit 0'" 42 | }, 43 | { "probability": 0.1, 44 | "value": "gcc -v" 45 | } 46 | ] 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /tsload/proc_starter/include/proc_starter.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_STARTER_H_ 2 | #define PROC_STARTER_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | struct proc_starter_workload { 11 | wlp_integer_t num_shells; 12 | char shell[1024]; 13 | }; 14 | 15 | struct proc_starter_data { 16 | squeue_t sq; 17 | }; 18 | 19 | struct proc_starter_request { 20 | char command[512]; 21 | 22 | }; 23 | #endif /* BUSY_WAIT_H_ */ 24 | 25 | -------------------------------------------------------------------------------- /tsload/proc_starter/include/shell.h: -------------------------------------------------------------------------------- 1 | #ifndef SHELL_H_ 2 | #define SHELL_H_ 3 | 4 | #define DEV_PTS_NAMELEN 32 5 | #define DEV_PTY_MASTER "/dev/ptmx" 6 | 7 | #define SHELL_BUF_SIZE 2048 8 | #define SHELL_WATERMARK 128 9 | 10 | #define SHELL_SETSID_ERROR 1 11 | #define SHELL_SLAVE_OPEN_ERROR 2 12 | #define SHELL_CLOSE_MASTER_ERROR 3 13 | #define SHELL_CLOSE_SLAVE_ERROR 4 14 | #define SHELL_DUP2_ERROR 5 15 | #define SHELL_EXECVE_ERROR 6 16 | #define SHELL_SIGRESET_ERROR 7 17 | #define SHELL_MKTERM_ERROR 8 18 | 19 | #define SHELL_TRACE_PTRS 0x01 20 | #define SHELL_TRACE_BUFS 0x02 21 | 22 | typedef struct pt_shell { 23 | int sh_pty; 24 | int sh_pid; 25 | 26 | char sh_pt_name[DEV_PTS_NAMELEN]; 27 | 28 | char* sh_buffer; 29 | size_t sh_buf_size; 30 | 31 | char* sh_start; 32 | char* sh_end; 33 | 34 | /* Variables used only in sh_create() */ 35 | int sh_pt_slave; 36 | /* Status PIPE. We couldn't track errors on child directly because 37 | * logging uses mutexes, and using they from forked processes is dangerous */ 38 | int sh_status_pipes[2]; 39 | } ps_shell_t; 40 | 41 | ps_shell_t* sh_create(const char* filename, char* const argv[], 42 | char* const envp[]); 43 | void sh_destroy(ps_shell_t* sh); 44 | 45 | char* sh_expect(ps_shell_t* sh, const char* line); 46 | 47 | #endif /* SHELL_H_ */ 48 | -------------------------------------------------------------------------------- /tsload/proc_starter/modinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proc_starter", 3 | "wlt_class": [ "os" ], 4 | "has_data": true, 5 | "params": { 6 | "num_shells": { 7 | "_type": "tsload.wlparam.WLParamInteger", 8 | "description": "Number of pre-forked shells. It is recommended to set this value to number of threads in threadpool.", 9 | "min": 1, 10 | "max": 1000 11 | }, 12 | "shell": { 13 | "_type": "tsload.wlparam.WLParamString", 14 | "optional": true, 15 | "default": "/usr/bin/sh", 16 | "description": "Path to pre-forked shell", 17 | "len": 1024 18 | }, 19 | "command": { 20 | "_type": "tsload.wlparam.WLParamString", 21 | "request": true, 22 | "description": "Command to be executed in request", 23 | "len": 512 24 | } 25 | } 26 | } --------------------------------------------------------------------------------