[users at bb.net] buildbot CPU usage

Francesco Di Mizio francescodimizio at gmail.com
Wed Aug 10 12:14:48 UTC 2016


Many thanks. Find it attached.

It's a tiny http server. People use a web page to send an HTTP request to
it to kick off builds.


On Wed, Aug 10, 2016 at 1:55 PM, Pierre Tardy <tardyp at gmail.com> wrote:

> I'll take a look.
>
> Le mer. 10 août 2016 12:45, Francesco Di Mizio <francescodimizio at gmail.com>
> a écrit :
>
>> I am starting to think it may be due to my own custom scheduler. If I
>> uploaded the code somewhere could anybody take a look? it's a 150 lines py
>> script.
>>
>> On Wed, Aug 10, 2016 at 12:10 PM, Francesco Di Mizio <
>> francescodimizio at gmail.com> wrote:
>>
>>> Thanks for that Pierre. I have it on on production and the performances
>>> do not degrade indeed.
>>>
>>> buildbot at e2123a50d1fb:~$ cat profile.txt
>>> No samples recorded.
>>>
>>> I need to study statprof a bit now ;)
>>>
>>> On Wed, Aug 10, 2016 at 11:20 AM, Vasily <vassnlit at gmail.com> wrote:
>>>
>>>> One can also use Intel® VTune™, you know... :-)
>>>>
>>>> Thanks,
>>>> Vasily
>>>> 09 авг. 2016 г. 23:56 пользователь "Pierre Tardy" <tardyp at gmail.com>
>>>> написал:
>>>>
>>>>> You can add following snippet to your master.cfg. It looks statprof
>>>>> does not work on osx, so I cannot test it to the end, but I know similar
>>>>> did work on my prod
>>>>>
>>>>>
>>>>> import statprof
>>>>> from  twisted.application.internet import TimerService
>>>>> from buildbot.util.service import BuildbotService
>>>>> class BuildbotTimerService(TimerService, BuildbotService):
>>>>>     name = "timer"
>>>>>     def __init__(self):
>>>>>         BuildbotService.__init__(self)
>>>>>         TimerService.__init__(self, 10, self.dump_stats)
>>>>>     def dump_stats(self):
>>>>>         statprof.stop()
>>>>>         with open("profile.txt", "w") as f:
>>>>>             statprof.display(f)
>>>>>         statprof.start()
>>>>>
>>>>> c['services'] = [ BuildbotTimerService() ]
>>>>>
>>>>>
>>>>> Le mar. 9 août 2016 à 21:44, Francesco Di Mizio <
>>>>> francescodimizio at gmail.com> a écrit :
>>>>>
>>>>>> Pierre,
>>>>>>
>>>>>> if you can enlighten on how you intend to use statprof, I will be
>>>>>> happy to give it a try.
>>>>>> This problem here is on top of my list right now as it's making my
>>>>>> prod env unusable.
>>>>>>
>>>>>> On Tue, Aug 9, 2016 at 8:25 PM, Pierre Tardy <tardyp at gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> Please note that cprofile is very intrusive and will lead to about
>>>>>>> 3x slower code.
>>>>>>>
>>>>>>> So I would not run that in prod. This is why I prefer statprof which
>>>>>>> is using a non intrusive method to get the stats
>>>>>>>
>>>>>>> Le mar. 9 août 2016 19:15, Francesco Di Mizio <
>>>>>>> francescodimizio at gmail.com> a écrit :
>>>>>>>
>>>>>>>> On a side note I have managed to get twistd to dump a blob I can
>>>>>>>> then load and analyze. Got to see if I can run this in production.
>>>>>>>> If anybody is curious the following worked for me:
>>>>>>>> twistd --savestats -n --profiler=cprofile  --profile=/crcdata/
>>>>>>>> profile.stats -y ./buildbot.tac
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Aug 9, 2016 at 5:21 PM, Francesco Di Mizio <
>>>>>>>> francescodimizio at gmail.com> wrote:
>>>>>>>>
>>>>>>>>> Well that's expected to an extent - p4 poller is running 'p4
>>>>>>>>> changes' every pollinterval seconds.
>>>>>>>>>
>>>>>>>>> Anyway just as a test I've tried to disable the poller and still
>>>>>>>>> at times I am seeing the CPU spiking up to above 100%. I believe I really
>>>>>>>>> need to profile this somehow.
>>>>>>>>>
>>>>>>>>> On Tue, Aug 9, 2016 at 5:08 PM, Dan Kegel <dank at kegel.com> wrote:
>>>>>>>>>
>>>>>>>>>> Also watch 'top' and see if poller processes are hogging
>>>>>>>>>> resources.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>
>>>>> _______________________________________________
>>>>> users mailing list
>>>>> users at buildbot.net
>>>>> https://lists.buildbot.net/mailman/listinfo/users
>>>>>
>>>>
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.buildbot.net/pipermail/users/attachments/20160810/09ca8cbf/attachment.html>
-------------- next part --------------
from buildbot.plugins import schedulers, util, steps
from buildbot.schedulers.base import BaseScheduler
from buildbot.schedulers.trysched import TryBase
from buildbot.process.properties import Properties
from buildbot.util import ascii2unicode
from twisted.web import http
from twisted.web import server
from twisted.internet import defer
from twisted.application import strports, service
from twisted.internet.protocol import Protocol
from twisted.python import log
from copy import deepcopy
import json
from . import config


class TryJobHTTPRequest(http.Request):
    def __init__(self, channel, queued):
        http.Request.__init__(self, channel, queued)
    @defer.inlineCallbacks
    def process(self):
        ret = None
        log.msg("Received URI: %s" % self.uri)
        try:
            # Support only one URI for now.
            if self.uri.find('send_try_patch') == -1:
                log.msg("Received invalid URI: %s" % self.uri)
                self.code = http.NOT_FOUND
                return
            print 'method %s' %self.method
            options = dict((k, v) for k, v in self.args.iteritems() if v)
            bsid, brids = yield self.channel.factory.parent.messageReceived(options)
            self.code = 200
        except Exception as e:
            log.msg(str(e))
        finally:
            self.code_message = http.RESPONSES[self.code]
            dict_to_ret = deepcopy(brids)
            dict_to_ret['bsid'] = bsid
            self.write(json.dumps(dict_to_ret))
            log.msg(json.dumps(dict_to_ret))
            self.finish()

class TryJobHTTP(TryBase, service.MultiService):
    """Opens a HTTP port to accept patch files and to execute these on the try server."""
   
    _PROPERTY_SOURCE = 'Try job'
    def __init__(self, port, name, builderNames, properties):
        TryBase.__init__(self, name, builderNames, properties)
        service.MultiService.__init__(self)
        if type(port) is int:
            port = "tcp:%d" % port
        self.port = port
        f = http.HTTPFactory()
        f.protocol.requestFactory = TryJobHTTPRequest
        f.parent = self
        s = strports.service(port, f)
        s.setServiceParent(self)
        log.msg('TryJobHTTP listening on port %s' % self.port)
  
    # We stop listening to incoming connections
    # When running reconfig this is not quick enough. Consider using a delay  (either here or on start up)
    # Right now it needs configure twice
    def stopService(self):
        service.MultiService.stopService(self)
        self.services[0].disownServiceParent()
	
    # Here we set all the properties not related to the sourcestamp
    def get_props(self, options, reason):    
        properties = {
                'branch' : options['branch'][0],
                #'git_branch' : 'master',
                'shelf' : options['p4_cl'][0],
                'owner' : options['email'][0],
                'email' : options['email'][0],
                'reason' : reason,
                'stream' : 'starcitizen' #maybe work this out in the future, should we change stream name
        }
        try:
            if (options['sync_mode'][0]):
                # sync_mode defaults to 'incremental' in the p4 step
                # so we only set it to 'full' if it's been passed by the try_page with its checkbox
                properties['sync_mode'] = 'full'
        except:
            pass
        props = Properties()
        props.updateFromProperties(self.properties)
        props.update(properties, self._PROPERTY_SOURCE)
        return props
    
    @defer.inlineCallbacks
    def messageReceived(self, options):
        who = options['who'][0]
        p4_cl = options['p4_cl'][0]
        branch = options['branch'][0]
        
        # we dont need to add a Change. This would be nice coz would allow us to see the changed files in the build page.
        # However it would require a scheduler (probably anybranch scedulers) for each builder.
        # what we have now is more simple: this http scheduler adding buildsets only with requested build requests
        # change_id = yield self.master.db.changes.addChange(
                                    # author=options['email'][0], 
                                    # comments='trybuild for %s for CL number %s' %(who, str(p4_cl)),
                                    # branch = branch
        # )
        # log.msg('pushed change_id %s' % change_id)        
        
        # We set Revision to None because we want to compile against latest
        revision = None 
        # changes = change_id
        patch_body = options['patch'][0]
        
        job_id = options['job_id'][0]
        email = options['email'][0]
	
        starcitizen_sourcestamp = dict(branch=branch,
                               codebase='starcitizen',
                               # revision=revision,
                               # patch_body=patch_body,
                               # patch_level=1,
                               # patch_author=email, #We do this so Mailnotifier will just use this (see Domain class in master.cfg)
                               # patch_comment='patch for shelf %s by %s' %(p4_cl, who),
                               # patch_subdir='',
                               project='',
                               repository=''
                              )
                              
        github_sourcestamp = dict(branch=config.git_branch,
                                       codebase='github',
                                       project='' ,
                                       repository=''
                                      )
	
        reason = u"try job"
        if who:
            reason += u" by user %s for CL %s" % (ascii2unicode(who), p4_cl)
        
        
        requested_builder_names = options['builder_names'][0].split(',')
        builder_names = TryBase.filterBuilderList(self, requested_builder_names)
        
        if not builder_names:
            log.msg("incoming Try job did specify at least one builder that does not exist")
            return
                                    
        bsid, brids = yield self.addBuildsetForSourceStamps(
                                    sourcestamps=[
                                        starcitizen_sourcestamp,
                                        github_sourcestamp
                                    ],
                                    reason=reason,
                                    external_idstring=ascii2unicode(job_id),
                                    builderNames=builder_names,
                                    properties=self.get_props(options, reason))
                                    
        defer.returnValue((bsid, brids))


More information about the users mailing list