You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
2.9 KiB

  1. # coding: utf-8
  2. # License AGPL-3 or later (http://www.gnu.org/licenses/lgpl).
  3. # Copyright 2014 Anybox <http://anybox.fr>
  4. # Copyright 2016 Vauxoo (https://www.vauxoo.com) <info@vauxoo.com>
  5. import logging
  6. import os
  7. from contextlib import contextmanager
  8. from cProfile import Profile
  9. import openerp
  10. from openerp import sql_db
  11. from openerp.http import WebRequest
  12. from openerp.service.server import ThreadedServer
  13. _logger = logging.getLogger(__name__)
  14. class CoreProfile(object):
  15. """The thread-shared profile object"""
  16. profile = None
  17. # Indicates if the whole profiling functionality is globally active or not.
  18. enabled = False
  19. @contextmanager
  20. def profiling():
  21. """Thread local profile management, according to the shared :data:`enabled`
  22. """
  23. if CoreProfile.enabled:
  24. CoreProfile.profile.enable()
  25. yield
  26. if CoreProfile.enabled:
  27. CoreProfile.profile.disable()
  28. def patch_odoo():
  29. """Modify Odoo entry points so that profile can record.
  30. Odoo is a multi-threaded program. Therefore, the :data:`profile` object
  31. needs to be enabled/disabled each in each thread to capture all the
  32. execution.
  33. For instance, Odoo spawns a new thread for each request.
  34. Modify database connect method to add options to enable postgresql logging
  35. based on PGOPTIONS environment variable
  36. """
  37. _logger.info('Patching openerp.http.WebRequest._call_function')
  38. webreq_f_origin = WebRequest._call_function
  39. def webreq_f(*args, **kwargs):
  40. with profiling():
  41. return webreq_f_origin(*args, **kwargs)
  42. WebRequest._call_function = webreq_f
  43. _logger.info('Patching openerp.sql_db.db_connect')
  44. db_connect_origin = sql_db.db_connect
  45. def dbconnect_f(to, *args, **kwargs):
  46. try:
  47. to += " options='%s' " % (os.environ['PGOPTIONS'] or '')
  48. except KeyError:
  49. pass
  50. return db_connect_origin(to, *args, **kwargs)
  51. sql_db.db_connect = dbconnect_f
  52. def dump_stats():
  53. """Dump stats to standard file"""
  54. _logger.info('Dump stats')
  55. CoreProfile.profile.dump_stats(
  56. os.path.expanduser('~/.openerp_server.stats'))
  57. def create_profile():
  58. """Create the global, shared profile object."""
  59. _logger.info('Create profile')
  60. CoreProfile.profile = Profile()
  61. def patch_stop():
  62. """When the server is stopped then save the result of cProfile stats"""
  63. origin_stop = ThreadedServer.stop
  64. _logger.info('Patching openerp.service.server.ThreadedServer.stop')
  65. def stop(*args, **kwargs):
  66. if openerp.tools.config['test_enable']:
  67. dump_stats()
  68. return origin_stop(*args, **kwargs)
  69. ThreadedServer.stop = stop
  70. def post_load():
  71. _logger.info('Post load')
  72. create_profile()
  73. patch_odoo()
  74. if openerp.tools.config['test_enable']:
  75. # Enable profile in test mode for orm methods.
  76. _logger.info('Enabling profiler and apply patch')
  77. CoreProfile.enabled = True
  78. patch_stop()