├── .gitignore ├── .hgignore ├── AUTHORS.rst ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── profiling_dashboard ├── __init__.py ├── forms.py ├── static │ └── profiling_dashboard │ │ └── css │ │ └── bootstrap.min.css ├── stats.py ├── templates │ └── profiling_dashboard │ │ ├── base.html │ │ ├── cpu.html │ │ ├── memory.html │ │ ├── process_info.html │ │ └── web_top.html ├── urls.py └── views.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | *.egg-info 4 | *.pyc -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | #IDE files 4 | .settings/* 5 | .project 6 | .pydevproject 7 | .cache/* 8 | 9 | #temp files 10 | *.pyc 11 | *.pyo 12 | *.orig 13 | *~ 14 | 15 | #misc files 16 | pip-log.txt 17 | 18 | #os files 19 | .DS_Store 20 | Thumbs.db 21 | 22 | #setup files 23 | build/ 24 | dist/ 25 | .build/ 26 | MANIFEST 27 | django_profiling_dashboard.egg-info -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | ------- 3 | 4 | * Mikhail Korobov 5 | * Mikhail Mezyakov (`@aluminiumgeek `_) 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Mikhail Korobov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the tastypie nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL MATT CROYDON BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt 2 | include *.rst 3 | recursive-include profiling_dashboard *.html *.css -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | django-profiling-dashboard 3 | ========================== 4 | 5 | django-profiling-dashboard provides a dashboard with various profiling tools suitable 6 | for use in live servers. 7 | 8 | Requirements 9 | ============ 10 | 11 | * `yappi `_ for thread-aware live server profiling 12 | that can be enabled and disabled at run time; 13 | * `Pympler `_ for memory debugging; 14 | * `psutil `_ for system resource usage investigation; 15 | * `django-query-exchange `_. 16 | 17 | Dashboard remplates are based on `Bootstrap `_ toolkit. 18 | 19 | django-profiling-dashboard requires django >= 1.5 and python >= 2.6. 20 | 21 | Installation 22 | ============ 23 | 24 | Make sure the requirements are installed:: 25 | 26 | pip install yappi pympler psutil 27 | pip install git+https://github.com/daevaorn/django-query-exchange.git#egg=django-query-exchange 28 | 29 | and install django-profiling-dashboard using pip:: 30 | 31 | pip install django-profiling-dashboard 32 | 33 | Usage 34 | ===== 35 | 36 | 1. Add ``'profiling_dashboard'`` and ``'query_exchange'`` to ``INSTALLED_APPS``:: 37 | 38 | INSTALLED_APPS = ( 39 | # ... 40 | 'query_exchange', 41 | 'profiling_dashboard', 42 | # ... 43 | ) 44 | 45 | 2. include 'profiling_dashboard.urls' in your urls.py:: 46 | 47 | urlpatterns = patterns('', 48 | # ... 49 | url(r'^profiling-dashboard/', include('profiling_dashboard.urls')), 50 | # ... 51 | ) 52 | 53 | 3. visit /profiling-dashboard/ 54 | 55 | Screenshots 56 | =========== 57 | 58 | TODO 59 | 60 | 61 | Notes on CPU profiling in multi-process environment 62 | =================================================== 63 | 64 | If there are several server processes then the profiler have to be started and stopped for each process, 65 | and the profiling stats will be different for different processes. 66 | 67 | In some deployment schemas (e.g. apache proxied by nginx) there is no way to make sure subsequent requests 68 | will be handled by the same server process so take this in account while using django-profiling-dashboard. 69 | -------------------------------------------------------------------------------- /profiling_dashboard/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import -------------------------------------------------------------------------------- /profiling_dashboard/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from pympler import muppy, summary 4 | from pympler.muppy import get_size 5 | from django import forms 6 | import yappi 7 | from profiling_dashboard.stats import get_top_info 8 | from .stats import get_full_yappi_stats, get_other_yappi_stats 9 | 10 | class YappiManageForm(forms.Form): 11 | ACTIONS = ( 12 | ('start', 'start'), 13 | ('start_with_builtins', 'start_with_builtins'), 14 | ('stop', 'stop'), 15 | ('reset', 'reset'), 16 | ) 17 | 18 | action = forms.ChoiceField(choices=ACTIONS) 19 | 20 | def do_action(self): 21 | action = self.cleaned_data['action'] 22 | if action == 'start': 23 | yappi.start() 24 | elif action == 'start_with_builtins': 25 | yappi.start(builtins=True) 26 | elif action == 'stop': 27 | yappi.stop() 28 | elif action == 'reset': 29 | yappi.clear_stats() 30 | return action 31 | 32 | 33 | class YappiFilterForm(forms.Form): 34 | SORT_ORDER = ( 35 | (yappi.SORT_ORDERS['ascending'], 'asc'), 36 | (yappi.SORT_ORDERS['descending'], 'desc'), 37 | ) 38 | 39 | SORT_TYPE = ( 40 | (yappi.SORT_TYPES_FUNCSTATS['name'], 'name of the function being profiled'), 41 | (yappi.SORT_TYPES_FUNCSTATS['ncall'], 'total call count of the function'), 42 | (yappi.SORT_TYPES_FUNCSTATS['tavg'], 'average total time'), 43 | (yappi.SORT_TYPES_FUNCSTATS['totaltime'], 'total time spent in the function excluding sub-calls'), 44 | (yappi.SORT_TYPES_FUNCSTATS['ttot'], 'total time spent in the function'), 45 | ) 46 | 47 | sort_order = forms.TypedChoiceField(choices=SORT_ORDER, initial=yappi.SORT_ORDERS['descending'], coerce=int, widget=forms.HiddenInput()) 48 | sort_type = forms.TypedChoiceField(choices=SORT_TYPE, initial=yappi.SORT_TYPES_FUNCSTATS['ttot'], coerce=int, widget=forms.HiddenInput()) 49 | limit = forms.IntegerField(initial=20, help_text='-1 means no limit') 50 | 51 | def get_stats(self): 52 | try: 53 | return get_full_yappi_stats( 54 | sorttype=self.cleaned_data['sort_type'], 55 | sortorder=self.cleaned_data['sort_order'], 56 | limit=self.cleaned_data['limit'], 57 | ) 58 | except Exception as e: 59 | return ['Stats are not available.\n Reason: %s' % e] 60 | 61 | def get_other_stats(self): 62 | return get_other_yappi_stats() 63 | 64 | 65 | class MuppyFilterForm(forms.Form): 66 | limit = forms.IntegerField(initial=20) 67 | sort_by = forms.IntegerField(initial=2, widget=forms.HiddenInput()) 68 | 69 | def get_report(self): 70 | all_objects = muppy.get_objects() 71 | size = get_size(all_objects) 72 | report = summary.summarize(all_objects) 73 | 74 | sort_index = self.cleaned_data['sort_by'] 75 | limit = self.cleaned_data['limit'] 76 | 77 | report.sort(key=lambda item: item[sort_index], reverse=True) 78 | if limit: 79 | report = report[:limit] 80 | 81 | return size, report 82 | 83 | class TopFilterForm(forms.Form): 84 | limit = forms.IntegerField(initial=100) 85 | sort_by = forms.CharField(initial='RSS', widget=forms.HiddenInput()) 86 | only_ready = forms.BooleanField(initial=True, required=False) 87 | 88 | def get_processes(self): 89 | processes = get_top_info() 90 | 91 | sort_index = self.cleaned_data['sort_by'] 92 | limit = self.cleaned_data['limit'] 93 | 94 | if self.cleaned_data['only_ready']: 95 | processes = filter(lambda proc: proc._READY, processes) 96 | 97 | processes = sorted(processes, key = lambda proc: getattr(proc, sort_index, None), reverse=True) 98 | if limit: 99 | processes = list(processes)[:limit] 100 | 101 | return processes 102 | -------------------------------------------------------------------------------- /profiling_dashboard/static/profiling_dashboard/css/bootstrap.min.css: -------------------------------------------------------------------------------- 1 | html,body{margin:0;padding:0;} 2 | h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;} 3 | table{border-collapse:collapse;border-spacing:0;} 4 | ol,ul{list-style:none;} 5 | q:before,q:after,blockquote:before,blockquote:after{content:"";} 6 | html{overflow-y:scroll;font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} 7 | a:focus{outline:thin dotted;} 8 | a:hover,a:active{outline:0;} 9 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} 10 | audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} 11 | audio:not([controls]){display:none;} 12 | sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;} 13 | sup{top:-0.5em;} 14 | sub{bottom:-0.25em;} 15 | img{border:0;-ms-interpolation-mode:bicubic;} 16 | button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;} 17 | button,input{line-height:normal;*overflow:visible;} 18 | button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;} 19 | button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} 20 | input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} 21 | input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;} 22 | textarea{overflow:auto;vertical-align:top;} 23 | body{background-color:#ffffff;margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;color:#404040;} 24 | .container{width:940px;margin-left:auto;margin-right:auto;zoom:1;}.container:before,.container:after{display:table;content:"";zoom:1;} 25 | .container:after{clear:both;} 26 | .container-fluid{position:relative;min-width:940px;padding-left:20px;padding-right:20px;zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";zoom:1;} 27 | .container-fluid:after{clear:both;} 28 | .container-fluid>.sidebar{position:absolute;top:0;left:20px;width:220px;} 29 | .container-fluid>.content{margin-left:240px;} 30 | a{color:#0069d6;text-decoration:none;line-height:inherit;font-weight:inherit;}a:hover{color:#00438a;text-decoration:underline;} 31 | .pull-right{float:right;} 32 | .pull-left{float:left;} 33 | .hide{display:none;} 34 | .show{display:block;} 35 | .row{zoom:1;margin-left:-20px;}.row:before,.row:after{display:table;content:"";zoom:1;} 36 | .row:after{clear:both;} 37 | .row>[class*="span"]{display:inline;float:left;margin-left:20px;} 38 | .span1{width:40px;} 39 | .span2{width:100px;} 40 | .span3{width:160px;} 41 | .span4{width:220px;} 42 | .span5{width:280px;} 43 | .span6{width:340px;} 44 | .span7{width:400px;} 45 | .span8{width:460px;} 46 | .span9{width:520px;} 47 | .span10{width:580px;} 48 | .span11{width:640px;} 49 | .span12{width:700px;} 50 | .span13{width:760px;} 51 | .span14{width:820px;} 52 | .span15{width:880px;} 53 | .span16{width:940px;} 54 | .span17{width:1000px;} 55 | .span18{width:1060px;} 56 | .span19{width:1120px;} 57 | .span20{width:1180px;} 58 | .span21{width:1240px;} 59 | .span22{width:1300px;} 60 | .span23{width:1360px;} 61 | .span24{width:1420px;} 62 | .row >.offset1{margin-left:80px;} 63 | .row >.offset2{margin-left:140px;} 64 | .row >.offset3{margin-left:200px;} 65 | .row >.offset4{margin-left:260px;} 66 | .row >.offset5{margin-left:320px;} 67 | .row >.offset6{margin-left:380px;} 68 | .row >.offset7{margin-left:440px;} 69 | .row >.offset8{margin-left:500px;} 70 | .row >.offset9{margin-left:560px;} 71 | .row >.offset10{margin-left:620px;} 72 | .row >.offset11{margin-left:680px;} 73 | .row >.offset12{margin-left:740px;} 74 | .span-one-third{width:300px;} 75 | .span-two-thirds{width:620px;} 76 | .offset-one-third{margin-left:340px;} 77 | .offset-two-thirds{margin-left:660px;} 78 | p{font-size:13px;font-weight:normal;line-height:18px;margin-bottom:9px;}p small{font-size:11px;color:#bfbfbf;} 79 | h1,h2,h3,h4,h5,h6{font-weight:bold;color:#404040;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#bfbfbf;} 80 | h1{margin-bottom:18px;font-size:30px;line-height:36px;}h1 small{font-size:18px;} 81 | h2{font-size:24px;line-height:36px;}h2 small{font-size:14px;} 82 | h3,h4,h5,h6{line-height:36px;} 83 | h3{font-size:18px;}h3 small{font-size:14px;} 84 | h4{font-size:16px;}h4 small{font-size:12px;} 85 | h5{font-size:14px;} 86 | h6{font-size:13px;color:#bfbfbf;text-transform:uppercase;} 87 | ul,ol{margin:0 0 18px 25px;} 88 | ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} 89 | ul{list-style:disc;} 90 | ol{list-style:decimal;} 91 | li{line-height:18px;color:#808080;} 92 | ul.unstyled{list-style:none;margin-left:0;} 93 | dl{margin-bottom:18px;}dl dt,dl dd{line-height:18px;} 94 | dl dt{font-weight:bold;} 95 | dl dd{margin-left:9px;} 96 | hr{margin:20px 0 19px;border:0;border-bottom:1px solid #eee;} 97 | strong{font-style:inherit;font-weight:bold;} 98 | em{font-style:italic;font-weight:inherit;line-height:inherit;} 99 | .muted{color:#bfbfbf;} 100 | blockquote{margin-bottom:18px;border-left:5px solid #eee;padding-left:15px;}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;} 101 | blockquote small{display:block;font-size:12px;font-weight:300;line-height:18px;color:#bfbfbf;}blockquote small:before{content:'\2014 \00A0';} 102 | address{display:block;line-height:18px;margin-bottom:18px;} 103 | code,pre{padding:0 3px 2px;font-family:Monaco, Andale Mono, Courier New, monospace;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} 104 | code{background-color:#fee9cc;color:rgba(0, 0, 0, 0.75);padding:1px 3px;} 105 | pre{background-color:#f5f5f5;display:block;padding:8.5px;margin:0 0 18px;line-height:18px;font-size:12px;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;white-space:pre;white-space:pre-wrap;word-wrap:break-word;} 106 | form{margin-bottom:18px;} 107 | fieldset{margin-bottom:18px;padding-top:18px;}fieldset legend{display:block;padding-left:150px;font-size:19.5px;line-height:1;color:#404040;*padding:0 0 5px 145px;*line-height:1.5;} 108 | form .clearfix{margin-bottom:18px;zoom:1;}form .clearfix:before,form .clearfix:after{display:table;content:"";zoom:1;} 109 | form .clearfix:after{clear:both;} 110 | label,input,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:normal;} 111 | label{padding-top:6px;font-size:13px;line-height:18px;float:left;width:130px;text-align:right;color:#404040;} 112 | form .input{margin-left:150px;} 113 | input[type=checkbox],input[type=radio]{cursor:pointer;} 114 | input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;font-size:13px;line-height:18px;color:#808080;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} 115 | select{padding:initial;} 116 | input[type=checkbox],input[type=radio]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:none;} 117 | input[type=file]{background-color:#ffffff;padding:initial;border:initial;line-height:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} 118 | input[type=button],input[type=reset],input[type=submit]{width:auto;height:auto;} 119 | select,input[type=file]{height:27px;*height:auto;line-height:27px;*margin-top:4px;} 120 | select[multiple]{height:inherit;background-color:#ffffff;} 121 | textarea{height:auto;} 122 | .uneditable-input{background-color:#ffffff;display:block;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;} 123 | :-moz-placeholder{color:#bfbfbf;} 124 | ::-webkit-input-placeholder{color:#bfbfbf;} 125 | input,textarea{-webkit-transform-style:preserve-3d;-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);} 126 | input:focus,textarea:focus{outline:0;border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);} 127 | input[type=file]:focus,input[type=checkbox]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:1px dotted #666;} 128 | form .clearfix.error>label,form .clearfix.error .help-block,form .clearfix.error .help-inline{color:#b94a48;} 129 | form .clearfix.error input,form .clearfix.error textarea{color:#b94a48;border-color:#ee5f5b;}form .clearfix.error input:focus,form .clearfix.error textarea:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} 130 | form .clearfix.error .input-prepend .add-on,form .clearfix.error .input-append .add-on{color:#b94a48;background-color:#fce6e6;border-color:#b94a48;} 131 | form .clearfix.warning>label,form .clearfix.warning .help-block,form .clearfix.warning .help-inline{color:#c09853;} 132 | form .clearfix.warning input,form .clearfix.warning textarea{color:#c09853;border-color:#ccae64;}form .clearfix.warning input:focus,form .clearfix.warning textarea:focus{border-color:#be9a3f;-webkit-box-shadow:0 0 6px #e5d6b1;-moz-box-shadow:0 0 6px #e5d6b1;box-shadow:0 0 6px #e5d6b1;} 133 | form .clearfix.warning .input-prepend .add-on,form .clearfix.warning .input-append .add-on{color:#c09853;background-color:#d2b877;border-color:#c09853;} 134 | form .clearfix.success>label,form .clearfix.success .help-block,form .clearfix.success .help-inline{color:#468847;} 135 | form .clearfix.success input,form .clearfix.success textarea{color:#468847;border-color:#57a957;}form .clearfix.success input:focus,form .clearfix.success textarea:focus{border-color:#458845;-webkit-box-shadow:0 0 6px #9acc9a;-moz-box-shadow:0 0 6px #9acc9a;box-shadow:0 0 6px #9acc9a;} 136 | form .clearfix.success .input-prepend .add-on,form .clearfix.success .input-append .add-on{color:#468847;background-color:#bcddbc;border-color:#468847;} 137 | .input-mini,input.mini,textarea.mini,select.mini{width:60px;} 138 | .input-small,input.small,textarea.small,select.small{width:90px;} 139 | .input-medium,input.medium,textarea.medium,select.medium{width:150px;} 140 | .input-large,input.large,textarea.large,select.large{width:210px;} 141 | .input-xlarge,input.xlarge,textarea.xlarge,select.xlarge{width:270px;} 142 | .input-xxlarge,input.xxlarge,textarea.xxlarge,select.xxlarge{width:530px;} 143 | textarea.xxlarge{overflow-y:auto;} 144 | input.span1,textarea.span1{display:inline-block;float:none;width:30px;margin-left:0;} 145 | input.span2,textarea.span2{display:inline-block;float:none;width:90px;margin-left:0;} 146 | input.span3,textarea.span3{display:inline-block;float:none;width:150px;margin-left:0;} 147 | input.span4,textarea.span4{display:inline-block;float:none;width:210px;margin-left:0;} 148 | input.span5,textarea.span5{display:inline-block;float:none;width:270px;margin-left:0;} 149 | input.span6,textarea.span6{display:inline-block;float:none;width:330px;margin-left:0;} 150 | input.span7,textarea.span7{display:inline-block;float:none;width:390px;margin-left:0;} 151 | input.span8,textarea.span8{display:inline-block;float:none;width:450px;margin-left:0;} 152 | input.span9,textarea.span9{display:inline-block;float:none;width:510px;margin-left:0;} 153 | input.span10,textarea.span10{display:inline-block;float:none;width:570px;margin-left:0;} 154 | input.span11,textarea.span11{display:inline-block;float:none;width:630px;margin-left:0;} 155 | input.span12,textarea.span12{display:inline-block;float:none;width:690px;margin-left:0;} 156 | input.span13,textarea.span13{display:inline-block;float:none;width:750px;margin-left:0;} 157 | input.span14,textarea.span14{display:inline-block;float:none;width:810px;margin-left:0;} 158 | input.span15,textarea.span15{display:inline-block;float:none;width:870px;margin-left:0;} 159 | input.span16,textarea.span16{display:inline-block;float:none;width:930px;margin-left:0;} 160 | input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;} 161 | .actions{background:#f5f5f5;margin-top:18px;margin-bottom:18px;padding:17px 20px 18px 150px;border-top:1px solid #ddd;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;}.actions .secondary-action{float:right;}.actions .secondary-action a{line-height:30px;}.actions .secondary-action a:hover{text-decoration:underline;} 162 | .help-inline,.help-block{font-size:13px;line-height:18px;color:#bfbfbf;} 163 | .help-inline{padding-left:5px;*position:relative;*top:-5px;} 164 | .help-block{display:block;max-width:600px;} 165 | .inline-inputs{color:#808080;}.inline-inputs span{padding:0 2px 0 1px;} 166 | .input-prepend input,.input-append input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} 167 | .input-prepend .add-on,.input-append .add-on{position:relative;background:#f5f5f5;border:1px solid #ccc;z-index:2;float:left;display:block;width:auto;min-width:16px;height:18px;padding:4px 4px 4px 5px;margin-right:-1px;font-weight:normal;line-height:18px;color:#bfbfbf;text-align:center;text-shadow:0 1px 0 #ffffff;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} 168 | .input-prepend .active,.input-append .active{background:#a9dba9;border-color:#46a546;} 169 | .input-prepend .add-on{*margin-top:1px;} 170 | .input-append input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} 171 | .input-append .add-on{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;margin-right:0;margin-left:-1px;} 172 | .inputs-list{margin:0 0 5px;width:100%;}.inputs-list li{display:block;padding:0;width:100%;} 173 | .inputs-list label{display:block;float:none;width:auto;padding:0;margin-left:20px;line-height:18px;text-align:left;white-space:normal;}.inputs-list label strong{color:#808080;} 174 | .inputs-list label small{font-size:11px;font-weight:normal;} 175 | .inputs-list .inputs-list{margin-left:25px;margin-bottom:10px;padding-top:0;} 176 | .inputs-list:first-child{padding-top:6px;} 177 | .inputs-list li+li{padding-top:2px;} 178 | .inputs-list input[type=radio],.inputs-list input[type=checkbox]{margin-bottom:0;margin-left:-20px;float:left;} 179 | .form-stacked{padding-left:20px;}.form-stacked fieldset{padding-top:9px;} 180 | .form-stacked legend{padding-left:0;} 181 | .form-stacked label{display:block;float:none;width:auto;font-weight:bold;text-align:left;line-height:20px;padding-top:0;} 182 | .form-stacked .clearfix{margin-bottom:9px;}.form-stacked .clearfix div.input{margin-left:0;} 183 | .form-stacked .inputs-list{margin-bottom:0;}.form-stacked .inputs-list li{padding-top:0;}.form-stacked .inputs-list li label{font-weight:normal;padding-top:0;} 184 | .form-stacked div.clearfix.error{padding-top:10px;padding-bottom:10px;padding-left:10px;margin-top:0;margin-left:-10px;} 185 | .form-stacked .actions{margin-left:-20px;padding-left:20px;} 186 | table{width:100%;margin-bottom:18px;padding:0;font-size:13px;border-collapse:collapse;}table th,table td{padding:10px 10px 9px;line-height:18px;text-align:left;} 187 | table th{padding-top:9px;font-weight:bold;vertical-align:middle;} 188 | table td{vertical-align:top;border-top:1px solid #ddd;} 189 | table tbody th{border-top:1px solid #ddd;vertical-align:top;} 190 | .condensed-table th,.condensed-table td{padding:5px 5px 4px;} 191 | .bordered-table{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.bordered-table th+th,.bordered-table td+td,.bordered-table th+td{border-left:1px solid #ddd;} 192 | .bordered-table thead tr:first-child th:first-child,.bordered-table tbody tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} 193 | .bordered-table thead tr:first-child th:last-child,.bordered-table tbody tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} 194 | .bordered-table tbody tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} 195 | .bordered-table tbody tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} 196 | table .span1{width:20px;} 197 | table .span2{width:60px;} 198 | table .span3{width:100px;} 199 | table .span4{width:140px;} 200 | table .span5{width:180px;} 201 | table .span6{width:220px;} 202 | table .span7{width:260px;} 203 | table .span8{width:300px;} 204 | table .span9{width:340px;} 205 | table .span10{width:380px;} 206 | table .span11{width:420px;} 207 | table .span12{width:460px;} 208 | table .span13{width:500px;} 209 | table .span14{width:540px;} 210 | table .span15{width:580px;} 211 | table .span16{width:620px;} 212 | .zebra-striped tbody tr:nth-child(odd) td,.zebra-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} 213 | .zebra-striped tbody tr:hover td,.zebra-striped tbody tr:hover th{background-color:#f5f5f5;} 214 | table .header{cursor:pointer;}table .header:after{content:"";float:right;margin-top:7px;border-width:0 4px 4px;border-style:solid;border-color:#000 transparent;visibility:hidden;} 215 | table .headerSortUp,table .headerSortDown{background-color:rgba(141, 192, 219, 0.25);text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);} 216 | table .header:hover:after{visibility:visible;} 217 | table .headerSortDown:after,table .headerSortDown:hover:after{visibility:visible;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} 218 | table .headerSortUp:after{border-bottom:none;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000;visibility:visible;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} 219 | table .blue{color:#049cdb;border-bottom-color:#049cdb;} 220 | table .headerSortUp.blue,table .headerSortDown.blue{background-color:#ade6fe;} 221 | table .green{color:#46a546;border-bottom-color:#46a546;} 222 | table .headerSortUp.green,table .headerSortDown.green{background-color:#cdeacd;} 223 | table .red{color:#9d261d;border-bottom-color:#9d261d;} 224 | table .headerSortUp.red,table .headerSortDown.red{background-color:#f4c8c5;} 225 | table .yellow{color:#ffc40d;border-bottom-color:#ffc40d;} 226 | table .headerSortUp.yellow,table .headerSortDown.yellow{background-color:#fff6d9;} 227 | table .orange{color:#f89406;border-bottom-color:#f89406;} 228 | table .headerSortUp.orange,table .headerSortDown.orange{background-color:#fee9cc;} 229 | table .purple{color:#7a43b6;border-bottom-color:#7a43b6;} 230 | table .headerSortUp.purple,table .headerSortDown.purple{background-color:#e2d5f0;} 231 | .topbar{height:40px;position:fixed;top:0;left:0;right:0;z-index:10000;overflow:visible;}.topbar a{color:#bfbfbf;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} 232 | .topbar h3 a:hover,.topbar .brand:hover,.topbar ul .active>a{background-color:#333;background-color:rgba(255, 255, 255, 0.05);color:#ffffff;text-decoration:none;} 233 | .topbar h3{position:relative;} 234 | .topbar h3 a,.topbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;color:#ffffff;font-size:20px;font-weight:200;line-height:1;} 235 | .topbar p{margin:0;line-height:40px;}.topbar p a:hover{background-color:transparent;color:#ffffff;} 236 | .topbar form{float:left;margin:5px 0 0 0;position:relative;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;} 237 | .topbar form.pull-right{float:right;} 238 | .topbar input{background-color:#444;background-color:rgba(255, 255, 255, 0.3);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:normal;font-weight:13px;line-height:1;padding:4px 9px;color:#ffffff;color:rgba(255, 255, 255, 0.75);border:1px solid #111;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-webkit-transform-style:preserve-3d;-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.topbar input:-moz-placeholder{color:#e6e6e6;} 239 | .topbar input::-webkit-input-placeholder{color:#e6e6e6;} 240 | .topbar input:hover{background-color:#bfbfbf;background-color:rgba(255, 255, 255, 0.5);color:#ffffff;} 241 | .topbar input:focus,.topbar input.focused{outline:0;background-color:#ffffff;color:#404040;text-shadow:0 1px 0 #ffffff;border:0;padding:5px 10px;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);} 242 | .topbar-inner,.topbar .fill{background-color:#222;background-color:#222222;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222));background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} 243 | .topbar div>ul,.nav{display:block;float:left;margin:0 10px 0 0;position:relative;left:0;}.topbar div>ul>li,.nav>li{display:block;float:left;} 244 | .topbar div>ul a,.nav a{display:block;float:none;padding:10px 10px 11px;line-height:19px;text-decoration:none;}.topbar div>ul a:hover,.nav a:hover{color:#ffffff;text-decoration:none;} 245 | .topbar div>ul .active>a,.nav .active>a{background-color:#222;background-color:rgba(0, 0, 0, 0.5);} 246 | .topbar div>ul.secondary-nav,.nav.secondary-nav{float:right;margin-left:10px;margin-right:0;}.topbar div>ul.secondary-nav .menu-dropdown,.nav.secondary-nav .menu-dropdown,.topbar div>ul.secondary-nav .dropdown-menu,.nav.secondary-nav .dropdown-menu{right:0;border:0;} 247 | .topbar div>ul a.menu:hover,.nav a.menu:hover,.topbar div>ul li.open .menu,.nav li.open .menu,.topbar div>ul .dropdown-toggle:hover,.nav .dropdown-toggle:hover,.topbar div>ul .dropdown.open .dropdown-toggle,.nav .dropdown.open .dropdown-toggle{background:#444;background:rgba(255, 255, 255, 0.05);} 248 | .topbar div>ul .menu-dropdown,.nav .menu-dropdown,.topbar div>ul .dropdown-menu,.nav .dropdown-menu{background-color:#333;}.topbar div>ul .menu-dropdown a.menu,.nav .menu-dropdown a.menu,.topbar div>ul .dropdown-menu a.menu,.nav .dropdown-menu a.menu,.topbar div>ul .menu-dropdown .dropdown-toggle,.nav .menu-dropdown .dropdown-toggle,.topbar div>ul .dropdown-menu .dropdown-toggle,.nav .dropdown-menu .dropdown-toggle{color:#ffffff;}.topbar div>ul .menu-dropdown a.menu.open,.nav .menu-dropdown a.menu.open,.topbar div>ul .dropdown-menu a.menu.open,.nav .dropdown-menu a.menu.open,.topbar div>ul .menu-dropdown .dropdown-toggle.open,.nav .menu-dropdown .dropdown-toggle.open,.topbar div>ul .dropdown-menu .dropdown-toggle.open,.nav .dropdown-menu .dropdown-toggle.open{background:#444;background:rgba(255, 255, 255, 0.05);} 249 | .topbar div>ul .menu-dropdown li a,.nav .menu-dropdown li a,.topbar div>ul .dropdown-menu li a,.nav .dropdown-menu li a{color:#999;text-shadow:0 1px 0 rgba(0, 0, 0, 0.5);}.topbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.topbar div>ul .dropdown-menu li a:hover,.nav .dropdown-menu li a:hover{background-color:#191919;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919));background-image:-moz-linear-gradient(top, #292929, #191919);background-image:-ms-linear-gradient(top, #292929, #191919);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919));background-image:-webkit-linear-gradient(top, #292929, #191919);background-image:-o-linear-gradient(top, #292929, #191919);background-image:linear-gradient(top, #292929, #191919);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0);color:#ffffff;} 250 | .topbar div>ul .menu-dropdown .active a,.nav .menu-dropdown .active a,.topbar div>ul .dropdown-menu .active a,.nav .dropdown-menu .active a{color:#ffffff;} 251 | .topbar div>ul .menu-dropdown .divider,.nav .menu-dropdown .divider,.topbar div>ul .dropdown-menu .divider,.nav .dropdown-menu .divider{background-color:#222;border-color:#444;} 252 | .topbar ul .menu-dropdown li a,.topbar ul .dropdown-menu li a{padding:4px 15px;} 253 | li.menu,.dropdown{position:relative;} 254 | a.menu:after,.dropdown-toggle:after{width:0;height:0;display:inline-block;content:"↓";text-indent:-99999px;vertical-align:top;margin-top:8px;margin-left:4px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #ffffff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;} 255 | .menu-dropdown,.dropdown-menu{background-color:#ffffff;float:left;display:none;position:absolute;top:40px;z-index:900;min-width:160px;max-width:220px;_width:160px;margin-left:0;margin-right:0;padding:6px 0;zoom:1;border-color:#999;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:0 1px 1px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.menu-dropdown li,.dropdown-menu li{float:none;display:block;background-color:none;} 256 | .menu-dropdown .divider,.dropdown-menu .divider{height:1px;margin:5px 0;overflow:hidden;background-color:#eee;border-bottom:1px solid #ffffff;} 257 | .topbar .dropdown-menu a,.dropdown-menu a{display:block;padding:4px 15px;clear:both;font-weight:normal;line-height:18px;color:#808080;text-shadow:0 1px 0 #ffffff;}.topbar .dropdown-menu a:hover,.dropdown-menu a:hover,.topbar .dropdown-menu a.hover,.dropdown-menu a.hover{background-color:#dddddd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd));background-image:-moz-linear-gradient(top, #eeeeee, #dddddd);background-image:-ms-linear-gradient(top, #eeeeee, #dddddd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd));background-image:-webkit-linear-gradient(top, #eeeeee, #dddddd);background-image:-o-linear-gradient(top, #eeeeee, #dddddd);background-image:linear-gradient(top, #eeeeee, #dddddd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0);color:#404040;text-decoration:none;-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);} 258 | .open .menu,.dropdown.open .menu,.open .dropdown-toggle,.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);} 259 | .open .menu-dropdown,.dropdown.open .menu-dropdown,.open .dropdown-menu,.dropdown.open .dropdown-menu{display:block;} 260 | .tabs,.pills{margin:0 0 18px;padding:0;list-style:none;zoom:1;}.tabs:before,.pills:before,.tabs:after,.pills:after{display:table;content:"";zoom:1;} 261 | .tabs:after,.pills:after{clear:both;} 262 | .tabs>li,.pills>li{float:left;}.tabs>li>a,.pills>li>a{display:block;} 263 | .tabs{border-color:#ddd;border-style:solid;border-width:0 0 1px;}.tabs>li{position:relative;margin-bottom:-1px;}.tabs>li>a{padding:0 15px;margin-right:2px;line-height:34px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.tabs>li>a:hover{text-decoration:none;background-color:#eee;border-color:#eee #eee #ddd;} 264 | .tabs .active>a,.tabs .active>a:hover{color:#808080;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} 265 | .tabs .menu-dropdown,.tabs .dropdown-menu{top:35px;border-width:1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;} 266 | .tabs a.menu:after,.tabs .dropdown-toggle:after{border-top-color:#999;margin-top:15px;margin-left:5px;} 267 | .tabs li.open.menu .menu,.tabs .open.dropdown .dropdown-toggle{border-color:#999;} 268 | .tabs li.open a.menu:after,.tabs .dropdown.open .dropdown-toggle:after{border-top-color:#555;} 269 | .pills a{margin:5px 3px 5px 0;padding:0 15px;line-height:30px;text-shadow:0 1px 1px #ffffff;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}.pills a:hover{color:#ffffff;text-decoration:none;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#00438a;} 270 | .pills .active a{color:#ffffff;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#0069d6;} 271 | .pills-vertical>li{float:none;} 272 | .tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} 273 | .tab-content>.active,.pill-content>.active{display:block;} 274 | .breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#f5f5f5;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5));background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline;text-shadow:0 1px 0 #ffffff;} 275 | .breadcrumb .divider{padding:0 5px;color:#bfbfbf;} 276 | .breadcrumb .active a{color:#404040;} 277 | .hero-unit{background-color:#f5f5f5;margin-bottom:30px;padding:60px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} 278 | .hero-unit p{font-size:18px;font-weight:200;line-height:27px;} 279 | footer{margin-top:17px;padding-top:17px;border-top:1px solid #eee;} 280 | .page-header{margin-bottom:17px;border-bottom:1px solid #ddd;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}.page-header h1{margin-bottom:8px;} 281 | .btn.danger,.alert-message.danger,.btn.danger:hover,.alert-message.danger:hover,.btn.error,.alert-message.error,.btn.error:hover,.alert-message.error:hover,.btn.success,.alert-message.success,.btn.success:hover,.alert-message.success:hover,.btn.info,.alert-message.info,.btn.info:hover,.alert-message.info:hover{color:#ffffff;} 282 | .btn .close,.alert-message .close{font-family:Arial,sans-serif;line-height:18px;} 283 | .btn.danger,.alert-message.danger,.btn.error,.alert-message.error{background-color:#c43c35;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#c43c35 #c43c35 #882a25;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} 284 | .btn.success,.alert-message.success{background-color:#57a957;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#57a957 #57a957 #3d773d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} 285 | .btn.info,.alert-message.info{background-color:#339bb9;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#339bb9 #339bb9 #22697d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} 286 | .btn{cursor:pointer;display:inline-block;background-color:#e6e6e6;background-repeat:no-repeat;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);padding:5px 14px 6px;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);color:#333;font-size:13px;line-height:normal;border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-webkit-transform-style:preserve-3d;-webkit-transition:0.1s linear all;-moz-transition:0.1s linear all;-ms-transition:0.1s linear all;-o-transition:0.1s linear all;transition:0.1s linear all;}.btn:hover{background-position:0 -15px;color:#333;text-decoration:none;} 287 | .btn:focus{outline:1px dotted #666;} 288 | .btn.primary{color:#ffffff;background-color:#0064cd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));background-image:-moz-linear-gradient(top, #049cdb, #0064cd);background-image:-ms-linear-gradient(top, #049cdb, #0064cd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));background-image:-webkit-linear-gradient(top, #049cdb, #0064cd);background-image:-o-linear-gradient(top, #049cdb, #0064cd);background-image:linear-gradient(top, #049cdb, #0064cd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#0064cd #0064cd #003f81;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} 289 | .btn.active,.btn :active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);} 290 | .btn.disabled{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} 291 | .btn[disabled]{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} 292 | .btn.large{font-size:15px;line-height:normal;padding:9px 14px 9px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} 293 | .btn.small{padding:7px 9px 7px;font-size:11px;} 294 | :root .alert-message,:root .btn{border-radius:0 \0;} 295 | button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0;} 296 | .close{float:right;color:#000000;font-size:20px;font-weight:bold;line-height:13.5px;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=25);-khtml-opacity:0.25;-moz-opacity:0.25;opacity:0.25;}.close:hover{color:#000000;text-decoration:none;filter:alpha(opacity=40);-khtml-opacity:0.4;-moz-opacity:0.4;opacity:0.4;} 297 | .alert-message{position:relative;padding:7px 15px;margin-bottom:18px;color:#404040;background-color:#eedc94;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));background-image:-moz-linear-gradient(top, #fceec1, #eedc94);background-image:-ms-linear-gradient(top, #fceec1, #eedc94);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));background-image:-webkit-linear-gradient(top, #fceec1, #eedc94);background-image:-o-linear-gradient(top, #fceec1, #eedc94);background-image:linear-gradient(top, #fceec1, #eedc94);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#eedc94 #eedc94 #e4c652;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);border-width:1px;border-style:solid;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);}.alert-message .close{margin-top:1px;*margin-top:0;} 298 | .alert-message a{font-weight:bold;color:#404040;} 299 | .alert-message.danger p a,.alert-message.error p a,.alert-message.success p a,.alert-message.info p a{color:#ffffff;} 300 | .alert-message h5{line-height:18px;} 301 | .alert-message p{margin-bottom:0;} 302 | .alert-message div{margin-top:5px;margin-bottom:2px;line-height:28px;} 303 | .alert-message .btn{-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);} 304 | .alert-message.block-message{background-image:none;background-color:#fdf5d9;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:14px;border-color:#fceec1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.alert-message.block-message ul,.alert-message.block-message p{margin-right:30px;} 305 | .alert-message.block-message ul{margin-bottom:0;} 306 | .alert-message.block-message li{color:#404040;} 307 | .alert-message.block-message .alert-actions{margin-top:5px;} 308 | .alert-message.block-message.error,.alert-message.block-message.success,.alert-message.block-message.info{color:#404040;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} 309 | .alert-message.block-message.error{background-color:#fddfde;border-color:#fbc7c6;} 310 | .alert-message.block-message.success{background-color:#d1eed1;border-color:#bfe7bf;} 311 | .alert-message.block-message.info{background-color:#ddf4fb;border-color:#c6edf9;} 312 | .alert-message.block-message.danger p a,.alert-message.block-message.error p a,.alert-message.block-message.success p a,.alert-message.block-message.info p a{color:#404040;} 313 | .pagination{height:36px;margin:18px 0;}.pagination ul{float:left;margin:0;border:1px solid #ddd;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} 314 | .pagination li{display:inline;} 315 | .pagination a{float:left;padding:0 14px;line-height:34px;border-right:1px solid;border-right-color:#ddd;border-right-color:rgba(0, 0, 0, 0.15);*border-right-color:#ddd;text-decoration:none;} 316 | .pagination a:hover,.pagination .active a{background-color:#c7eefe;} 317 | .pagination .disabled a,.pagination .disabled a:hover{background-color:transparent;color:#bfbfbf;} 318 | .pagination .next a{border:0;} 319 | .well{background-color:#f5f5f5;margin-bottom:20px;padding:19px;min-height:20px;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} 320 | .modal-backdrop{background-color:#000000;position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000;}.modal-backdrop.fade{opacity:0;} 321 | .modal-backdrop,.modal-backdrop.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;} 322 | .modal{position:fixed;top:50%;left:50%;z-index:11000;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal .close{margin-top:7px;} 323 | .modal.fade{-webkit-transform-style:preserve-3d;-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} 324 | .modal.fade.in{top:50%;} 325 | .modal-header{border-bottom:1px solid #eee;padding:5px 15px;} 326 | .modal-body{padding:15px;} 327 | .modal-body form{margin-bottom:0;} 328 | .modal-footer{background-color:#f5f5f5;padding:14px 15px 15px;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;zoom:1;margin-bottom:0;}.modal-footer:before,.modal-footer:after{display:table;content:"";zoom:1;} 329 | .modal-footer:after{clear:both;} 330 | .modal-footer .btn{float:right;margin-left:5px;} 331 | .modal .popover,.modal .twipsy{z-index:12000;} 332 | .twipsy{display:block;position:absolute;visibility:visible;padding:5px;font-size:11px;z-index:1000;filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}.twipsy.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;} 333 | .twipsy.above .twipsy-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} 334 | .twipsy.left .twipsy-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} 335 | .twipsy.below .twipsy-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} 336 | .twipsy.right .twipsy-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} 337 | .twipsy-inner{padding:3px 8px;background-color:#000000;color:white;text-align:center;max-width:200px;text-decoration:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} 338 | .twipsy-arrow{position:absolute;width:0;height:0;} 339 | .popover{position:absolute;top:0;left:0;z-index:1000;padding:5px;display:none;}.popover.above .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} 340 | .popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} 341 | .popover.below .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} 342 | .popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} 343 | .popover .arrow{position:absolute;width:0;height:0;} 344 | .popover .inner{background:#000000;background:rgba(0, 0, 0, 0.8);padding:3px;overflow:hidden;width:280px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} 345 | .popover .title{background-color:#f5f5f5;padding:9px 15px;line-height:1;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-bottom:1px solid #eee;} 346 | .popover .content{background-color:#ffffff;padding:14px;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover .content p,.popover .content ul,.popover .content ol{margin-bottom:0;} 347 | .fade{-webkit-transform-style:preserve-3d;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;} 348 | .label{padding:1px 3px 2px;font-size:9.75px;font-weight:bold;color:#ffffff;text-transform:uppercase;white-space:nowrap;background-color:#bfbfbf;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}.label.important{background-color:#c43c35;} 349 | .label.warning{background-color:#f89406;} 350 | .label.success{background-color:#46a546;} 351 | .label.notice{background-color:#62cffc;} 352 | .media-grid{margin-left:-20px;margin-bottom:0;zoom:1;}.media-grid:before,.media-grid:after{display:table;content:"";zoom:1;} 353 | .media-grid:after{clear:both;} 354 | .media-grid li{display:inline;} 355 | .media-grid a{float:left;padding:4px;margin:0 0 18px 20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}.media-grid a img{display:block;} 356 | .media-grid a:hover{border-color:#0069d6;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);} 357 | -------------------------------------------------------------------------------- /profiling_dashboard/stats.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from collections import namedtuple 4 | from contextlib import contextmanager 5 | import datetime 6 | from time import strftime 7 | import psutil 8 | import yappi 9 | 10 | YappiStat = namedtuple('YappiStat', 'name ncall ttotal tsub tavg') 11 | 12 | def _get_all_yappi_stats(): 13 | res = [] 14 | def handle(data): 15 | avg = data[2]/data[1] 16 | stat = YappiStat(*(data+(avg,))) 17 | res.append(stat) 18 | yappi.enum_stats(handle) 19 | return res 20 | 21 | def get_full_yappi_stats(sorttype=yappi.SORT_TYPES_FUNCSTATS['ncall'], sortorder=yappi.SORT_ORDERS['descending'], limit=None): 22 | """ 23 | Like yappi.get_stats, but returns a list of namedtuples with the full information for function profiling data. 24 | """ 25 | 26 | stats = _get_all_yappi_stats() 27 | stats.sort(key=lambda stat: stat[sorttype], reverse=sortorder) 28 | 29 | if limit is not None: 30 | stats = stats[:limit] 31 | 32 | return stats 33 | 34 | def get_other_yappi_stats(): 35 | stats = yappi.get_stats(limit=0) 36 | THREAD_HEADER = '\n\nname tid fname scnt ttot' 37 | thread_index = stats.index(THREAD_HEADER) 38 | stats[thread_index] = stats[thread_index].strip() 39 | return stats[thread_index:] 40 | 41 | def get_yappi_status(): 42 | try: 43 | other_stats = get_other_yappi_stats() 44 | last_line = other_stats[-1] 45 | return last_line.split()[0] 46 | except Exception as e: 47 | return str(e) 48 | 49 | @contextmanager 50 | def _ignore_AccessDenied(): 51 | try: 52 | yield 53 | except psutil.AccessDenied: 54 | pass 55 | except AttributeError: 56 | pass 57 | 58 | def proc_annotate_with_short_info(p): 59 | p._READY = False 60 | with _ignore_AccessDenied(): 61 | p.PID = p.pid 62 | p.USERNAME = p.username 63 | p.CMDLINE = ' '.join(p.cmdline()) 64 | p.NAME = p.name 65 | p.RSS = p.memory_info().rss 66 | p.VMS = p.memory_info().vms 67 | p.MEMPERCENT = p.memory_percent() 68 | p.CPUPERCENT = p.cpu_percent(interval=0) 69 | p.CPU_USER = p.cpu_times().user 70 | p.CPU_SYSTEM = p.cpu_times().system 71 | p._READY = True 72 | return p 73 | 74 | def proc_annotate_with_full_info(p): 75 | 76 | with _ignore_AccessDenied(): 77 | p.CREATE_TIMESTAMP = p.create_time 78 | p.CREATE_TIME = datetime.datetime.fromtimestamp(p.CREATE_TIMESTAMP()) 79 | p.NOW = datetime.datetime.now() 80 | 81 | with _ignore_AccessDenied(): 82 | p.CPUPERCENT = p.cpu_percent(interval=1) 83 | 84 | with _ignore_AccessDenied(): 85 | p.IO_COUNTERS = p.io_counters() 86 | 87 | with _ignore_AccessDenied(): 88 | p.THREADS = p.threads() 89 | 90 | with _ignore_AccessDenied(): 91 | p.OPEN_FILES = p.open_files() 92 | 93 | with _ignore_AccessDenied(): 94 | p.CONNECTIONS = p.connections() 95 | 96 | def get_top_info(): 97 | processes_iter = psutil.process_iter() 98 | processes = [] 99 | 100 | for p in processes_iter: 101 | try: 102 | processes.append(proc_annotate_with_short_info(p)) 103 | except psutil.NoSuchProcess: 104 | processes.remove(p) 105 | 106 | return processes 107 | -------------------------------------------------------------------------------- /profiling_dashboard/templates/profiling_dashboard/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | 6 | 7 | 12 | {% block title %}Django Profiling Dashboard{% endblock %} 13 | {% block extrahead %}{% endblock %} 14 | 15 | 16 | 17 | 33 | 34 | {% if messages %} 35 | {% for message in messages %} 36 |
37 |

{{ message }}

38 |
39 | {% endfor %} 40 | {% endif %} 41 | 42 |
43 | {% block content %}{% endblock %} 44 |
45 | 46 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /profiling_dashboard/templates/profiling_dashboard/cpu.html: -------------------------------------------------------------------------------- 1 | {% extends "profiling_dashboard/base.html" %} 2 | {% load query_exchange_tags %} 3 | 4 | {% block nav-cpu %} 5 |
  • CPU profiler
  • 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
    {% csrf_token %} 10 |
    11 |
    12 |

    {% if status %}Status: {{ status }}{% endif %}

    13 | 14 | 15 | 16 | 17 |
    18 |
    19 |
    20 | 21 |
    22 | {{ form.sort_type }} 23 | {{ form.sort_order }} 24 |
    25 | {{ form.limit.label_tag }} 26 |
    27 | {{ form.limit }} 28 | {{ form.limit.errors }} 29 | 30 |
    31 |
    32 |
    33 | 34 | {% if other_stats %} 35 |
    {% for stat in other_stats %}
    36 | {{ stat }}{% endfor %}
    37 | {% endif %} 38 | 39 | {% if stats %} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {% for stat in stats %} 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {% endfor %} 59 |
    namencallttotaltsubtavg
    {{ stat.name }}{{ stat.ncall }}{{ stat.ttotal }}{{ stat.tsub }}{{ stat.tavg }}
    60 | {% endif %} 61 | 62 | {% endblock %} 63 | -------------------------------------------------------------------------------- /profiling_dashboard/templates/profiling_dashboard/memory.html: -------------------------------------------------------------------------------- 1 | {% extends "profiling_dashboard/base.html" %} 2 | {% load query_exchange_tags %} 3 | 4 | {% block nav-memory %} 5 |
  • Python memory profiler
  • 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
    10 |
    11 | {{ form.sort_by }} 12 | 13 |
    14 | {{ form.limit.label_tag }} 15 |
    16 | {{ form.limit }} 17 | {{ form.limit.errors }} 18 | 19 |
    20 |
    21 |
    22 | 23 | {% if form.errors %} 24 | {{ form.errors }} 25 | {% endif %} 26 | 27 | {% if size %}

    Total size: {{ size|filesizeformat }}

    {% endif %} 28 | 29 | {% if report %} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% for row in report %} 39 | 40 | 41 | 42 | 43 | 44 | {% endfor %} 45 |
    typenumbersize
    {{ row.0 }}{{ row.1 }}{{ row.2|filesizeformat }}
    46 | {% endif %} 47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /profiling_dashboard/templates/profiling_dashboard/process_info.html: -------------------------------------------------------------------------------- 1 | {% extends "profiling_dashboard/web_top.html" %} 2 | {% load query_exchange_tags %} 3 | 4 | {% block content %} 5 |
    6 |

    {{ proc.NAME }} (pid: {{ proc.PID }})

    7 | 8 |

    General Info

    9 |
    10 |
    CMDLINE
    11 |
    {{ proc.CMDLINE }}
    12 | 13 |
    USERNAME
    14 |
    {{ proc.USERNAME }}
    15 | 16 |
    MEMORY: VMS / RSS / %MEM
    17 |
    {{ proc.VMS|filesizeformat }} / {{ proc.RSS|filesizeformat }} / {{ proc.MEMPERCENT }}
    18 | 19 |
    CPU: USER / SYSTEM / %CPU
    20 |
    {{ proc.CPU_USER }} / {{ proc.CPU_SYSTEM }} / {{ proc.CPUPERCENT }}
    21 | 22 |
    CREATE TIME (now is {{ proc.NOW }})
    23 |
    {{ proc.CREATE_TIME }}
    24 | 25 | {% if proc.IO_COUNTERS %} 26 |
    IO: read / write (size)
    27 |
    {{ proc.IO_COUNTERS.read_bytes|filesizeformat }} / {{ proc.IO_COUNTERS.write_bytes|filesizeformat }}
    28 | 29 |
    IO: read / write (count)
    30 |
    {{ proc.IO_COUNTERS.read_count }} / {{ proc.IO_COUNTERS.write_count }}
    31 | {% endif %} 32 |
    33 | 34 | {% if proc.THREADS %} 35 |

    Threads

    36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | {% for thread in proc.THREADS %} 45 | 46 | 47 | 48 | 49 | 50 | {% endfor %} 51 |
    iduser timesystem time
    {{ thread.id }}{{ thread.user_time }}{{ thread.system_time }}
    52 | {% endif %} 53 | 54 | {% if proc.CONNECTIONS %} 55 |

    Connections

    56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {% for conn in proc.CONNECTIONS %} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {% endfor %} 79 |
    fdfamilytypelocal addressremote addressstatus
    {{ conn.fd }}{{ conn.family }}{{ conn.type }}{{ conn.local_address.0 }}:{{ conn.local_address.1 }}{{ conn.remote_address.0 }}:{{ conn.remote_address.1 }}{{ conn.status }}
    80 | {% endif %} 81 | 82 | {% if proc.OPEN_FILES %} 83 |

    Open Files

    84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | {% for file in proc.OPEN_FILES %} 93 | 94 | 95 | 96 | 97 | {% endfor %} 98 |
    fdpath
    {{ file.fd }}{{ file.path }}
    99 | 100 | {% endif %} 101 | 102 |
    103 | {% endblock %} -------------------------------------------------------------------------------- /profiling_dashboard/templates/profiling_dashboard/web_top.html: -------------------------------------------------------------------------------- 1 | {% extends "profiling_dashboard/base.html" %} 2 | {% load query_exchange_tags %} 3 | 4 | {% block nav-top %} 5 |
  • top
  • 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
    10 |
    11 | {{ form.sort_by }} 12 | 13 |
    14 |
    15 | 20 |
    21 |
    22 | 23 |
    24 | {{ form.limit.label_tag }} 25 |
    26 | {{ form.limit }} 27 | {{ form.limit.errors }} 28 | 29 |
    30 |
    31 |
    32 | 33 | {% if form.errors %} 34 | {{ form.errors }} 35 | {% endif %} 36 | 37 | {% if size %}

    Total size: {{ size|filesizeformat }}

    {% endif %} 38 | 39 | {% if processes %} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {% for proc in processes %} 56 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {% endfor %} 73 |
    PIDUSERVIRTRES%CPU%MEMCPU userCPU systemNAMECOMMAND
    58 | 59 | {% if proc.PID|lower == pid|lower %}→ {{ proc.PID }}{% else %}{{ proc.PID }}{% endif %} 60 | 61 | {{ proc.USERNAME }}{{ proc.VMS|filesizeformat }}{{ proc.RSS|filesizeformat }}{{ proc.CPUPERCENT }}{{ proc.MEMPERCENT }}{{ proc.CPU_USER }}{{ proc.CPU_SYSTEM }}{{ proc.NAME }}{{ proc.CMDLINE }}
    74 | {% endif %} 75 | {% endblock %} 76 | -------------------------------------------------------------------------------- /profiling_dashboard/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from django.conf.urls import url 4 | from . import views 5 | 6 | urlpatterns = [ 7 | url(r'^$', views.yappi_stats, name='profiling_yappi_stats'), 8 | url(r'^do$', views.yappi_manage, name='profiling_yappi_manage'), 9 | url(r'^memory-usage$', views.memory_usage, name='profiling_memory_usage'), 10 | url(r'^top/$', views.web_top, name='profiling_web_top'), 11 | url(r'^top/(?P\d+)$', views.process_info, name='profiling_process_info') 12 | ] 13 | -------------------------------------------------------------------------------- /profiling_dashboard/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | import os 4 | import threading 5 | import psutil 6 | from django.shortcuts import redirect 7 | from django.template.response import TemplateResponse 8 | from django.contrib.admin.views.decorators import staff_member_required 9 | from django.contrib import messages 10 | from .forms import YappiManageForm, YappiFilterForm, MuppyFilterForm, TopFilterForm 11 | from .stats import get_yappi_status, proc_annotate_with_short_info, proc_annotate_with_full_info 12 | 13 | def _tid_safe(): 14 | try: 15 | return threading.currentThread().ident 16 | except Exception as e: 17 | return str(e) 18 | 19 | 20 | @staff_member_required 21 | def yappi_manage(request): 22 | form = YappiManageForm(request.POST or None) 23 | if form.is_valid(): 24 | pid = os.getpid() 25 | try: 26 | action = form.do_action() 27 | messages.success(request, "yappi %s is successful for process #%s" % (action, pid)) 28 | except Exception as e: 29 | messages.info(request, "process #%s" % pid) 30 | messages.error(request, e) 31 | return redirect('profiling_yappi_stats') 32 | 33 | 34 | @staff_member_required 35 | def yappi_stats(request): 36 | form = YappiFilterForm(request.GET or None) 37 | stats, other_stats = [], [] 38 | status = get_yappi_status() 39 | pid = os.getpid() 40 | if form.is_valid(): 41 | try: 42 | stats = form.get_stats() 43 | other_stats = form.get_other_stats() 44 | except Exception as e: 45 | messages.info(request, "process #%s" % pid) 46 | messages.error(request, e) 47 | 48 | return TemplateResponse(request, 'profiling_dashboard/cpu.html', { 49 | 'form': form, 50 | 'stats': stats, 51 | 'other_stats': other_stats, 52 | 'status': status, 53 | 'pid': pid, 54 | 'tid': _tid_safe(), 55 | }) 56 | 57 | 58 | @staff_member_required 59 | def memory_usage(request): 60 | pid = os.getpid() 61 | 62 | form = MuppyFilterForm(request.GET or None) 63 | size, report = None, None 64 | if form.is_valid(): 65 | size, report = form.get_report() 66 | 67 | return TemplateResponse(request, 'profiling_dashboard/memory.html', { 68 | 'form': form, 69 | 'size': size, 70 | 'report': report, 71 | 'pid': pid, 72 | 'tid': _tid_safe(), 73 | }) 74 | 75 | @staff_member_required 76 | def web_top(request): 77 | pid = os.getpid() 78 | form = TopFilterForm(request.GET or None) 79 | 80 | processes = [] 81 | if form.is_valid(): 82 | processes = form.get_processes() 83 | 84 | return TemplateResponse(request, 'profiling_dashboard/web_top.html', { 85 | 'pid': pid, 86 | 'processes': processes, 87 | 'form': form, 88 | 'tid': _tid_safe(), 89 | }) 90 | 91 | @staff_member_required 92 | def process_info(request, pid): 93 | try: 94 | proc = psutil.Process(int(pid)) 95 | except Exception as e: 96 | messages.error(request, "process %s: %s" % (pid, e)) 97 | return redirect('profiling_web_top') 98 | 99 | proc_annotate_with_short_info(proc) 100 | proc_annotate_with_full_info(proc) 101 | 102 | return TemplateResponse(request, 'profiling_dashboard/process_info.html', { 103 | 'pid': os.getpid(), 104 | 'tid': _tid_safe(), 105 | 'proc': proc, 106 | }) -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | version='0.2' 5 | 6 | setup( 7 | name='django-profiling-dashboard', 8 | version=version, 9 | author='Mikhail Korobov', 10 | author_email='kmike84@gmail.com', 11 | 12 | packages=['profiling_dashboard'], 13 | package_data={ 14 | 'profiling_dashboard': ['templates/profiling_dashboard/*.html', 'static/profiling_dashboard/css/*.css'] 15 | }, 16 | 17 | url='https://github.com/kmike/django-profiling-dashboard', 18 | license = 'MIT license', 19 | description = """ Django profiling dashboard for debugging CPU, memory and other resources usage in live servers """, 20 | 21 | long_description = open('README.rst').read(), 22 | requires = ['django (>= 1.5)', 'yappi (>= 0.54)', 'psutil (>= 0.4.1)', 'pympler (>= 0.2.1)', 'query_exchange'], 23 | 24 | classifiers=( 25 | 'Development Status :: 3 - Alpha', 26 | 'Environment :: Web Environment', 27 | 'Framework :: Django', 28 | 'Intended Audience :: Developers', 29 | 'Intended Audience :: System Administrators', 30 | 'License :: OSI Approved :: MIT License', 31 | 'Programming Language :: Python', 32 | 'Programming Language :: Python :: 2.6', 33 | 'Programming Language :: Python :: 2.7', 34 | 'Programming Language :: Python :: Implementation :: CPython', 35 | 'Topic :: Software Development :: Libraries :: Python Modules', 36 | ), 37 | ) 38 | --------------------------------------------------------------------------------